binutils-gdb/gdb/remote-fileio.c
Pedro Alves 6b8edb5101 remote: one struct remote_state per struct remote_target
'struct remote_state' today contains per-connection state, however
there's only a single global instance of that type.  In order to
support multiple connections, we must have one such object per
connection.

Thus this patch eliminates the 'remote_state' global in favor of
having a remote_state instance per remote_target instance.

The get_remote_state free function is eliminated as well, by making it
a remote_target method instead.

The patch then fixes the fallout by making all free functions that
refer to get_remote_state() directly or indirectly be methods of
remote_target too.

Likewise, remote-fileio.c and remote-notif.c routines are
parameterized with a remote_target pointer too, so they can call into
the right remote_target instance.

References to the global 'get_remote_state ()->remote_desc' to tell
whether the remote target is open (!= nullptr) must be replaced with
something else:

 - Command implementations use a new get_current_remote_target free
 function.

 - remote_target::open_1 checks the exception type instead.

Finally, remote_target and extended_remote_target are made
heap-allocated targets.  As with the earlier core target patches, it
still won't be possible to have more than one remote_target instance
in practice, but this puts us closer.

gdb/ChangeLog:
2018-05-22  Pedro Alves  <palves@redhat.com>

	* remote-fileio.c (remote_fileio_reply, remote_fileio_ioerror)
	(remote_fileio_badfd, remote_fileio_return_errno)
	(remote_fileio_return_success, remote_fileio_func_open)
	(remote_fileio_func_open, remote_fileio_func_close)
	(remote_fileio_func_read, remote_fileio_func_write)
	(remote_fileio_func_lseek, remote_fileio_func_rename)
	(remote_fileio_func_unlink, remote_fileio_func_stat)
	(remote_fileio_func_fstat, remote_fileio_func_gettimeofday)
	(remote_fileio_func_isatty, remote_fileio_func_system): Add
	remote_target parameter.
	(remote_fio_func_map) <func>: Add remote_target parameter.
	(do_remote_fileio_request, remote_fileio_request):
	* remote-fileio.h (remote_fileio_request):
	* remote-notif.c (remote_notif_ack, remote_notif_parse, ): Add
	remote_target parameter.
	(remote_notif_process, handle_notification): Adjust to pass down
	the remote.
	(remote_notif_state_allocate): Add remote_target parameter.  Save
	it.
	* remote-notif.h (struct remote_target): Forward declare.
	(struct notif_client) <parse, ack, can_get_pending_events>: Add
	remote_target parameter.
	(struct remote_notif_state) <remote>: New field.
	(remote_notif_ack, remote_notif_parse): Add remote_target
	parameter.
	(remote_notif_state_allocate, remote_notif_state_allocate): Add
	remote_target parameter.
	* remote.c (OPAQUETHREADBYTES, threadref, gdb_ext_thread_info)
	(threads_listing_context, rmt_thread_action, protocol_feature)
	(packet_reg, stop_reply, stop_reply_p, enum packet_support)
	(packet_result, struct threads_listing_context, remote_state):
	Move definitions and declarations higher up.
	(remote_target) <~remote_target>: Declare.
	(remote_download_command_source, remote_file_put, remote_file_get)
	(remote_file_delete, remote_hostio_pread, remote_hostio_pwrite)
	(remote_hostio_pread_vFile, remote_hostio_send_command)
	(remote_hostio_set_filesystem, remote_hostio_open)
	(remote_hostio_close, remote_hostio_unlink, remote_state)
	(get_remote_state, get_remote_packet_size, get_memory_packet_size)
	(get_memory_write_packet_size, get_memory_read_packet_size)
	(append_pending_thread_resumptions, remote_detach_1)
	(append_resumption, remote_resume_with_vcont)
	(add_current_inferior_and_thread, wait_ns, wait_as)
	(process_stop_reply, remote_notice_new_inferior)
	(process_initial_stop_replies, remote_add_thread)
	(btrace_sync_conf, remote_btrace_maybe_reopen)
	(remove_new_fork_children, kill_new_fork_children)
	(discard_pending_stop_replies, stop_reply_queue_length)
	(check_pending_events_prevent_wildcard_vcont)
	(discard_pending_stop_replies_in_queue, stop_reply)
	(remote_notif_remove_queued_reply, stop_reply *queued_stop_reply)
	(peek_stop_reply, remote_parse_stop_reply, remote_stop_ns)
	(remote_interrupt_as, remote_interrupt_ns)
	(remote_get_noisy_reply, remote_query_attached)
	(remote_add_inferior, remote_current_thread, get_current_thread)
	(set_thread, set_general_thread, set_continue_thread)
	(set_general_process, write_ptid)
	(remote_unpack_thread_info_response, remote_get_threadinfo)
	(parse_threadlist_response, remote_get_threadlist)
	(remote_threadlist_iterator, remote_get_threads_with_ql)
	(remote_get_threads_with_qxfer)
	(remote_get_threads_with_qthreadinfo, extended_remote_restart)
	(get_offsets, remote_check_symbols, remote_supported_packet)
	(remote_query_supported, remote_packet_size)
	(remote_serial_quit_handler, remote_detach_pid)
	(remote_vcont_probe, remote_resume_with_hc)
	(send_interrupt_sequence, interrupt_query)
	(remote_notif_get_pending_events, fetch_register_using_p)
	(send_g_packet, process_g_packet, fetch_registers_using_g)
	(store_register_using_P, store_registers_using_G)
	(set_remote_traceframe, check_binary_download)
	(remote_write_bytes_aux, remote_write_bytes, remote_read_bytes_1)
	(remote_xfer_live_readonly_partial, remote_read_bytes)
	(remote_send_printf, remote_flash_write, readchar)
	(remote_serial_write, putpkt, putpkt_binary, skip_frame)
	(read_frame, getpkt, getpkt_or_notif_sane_1, getpkt_sane)
	(getpkt_or_notif_sane, remote_vkill, remote_kill_k)
	(extended_remote_disable_randomization, extended_remote_run)
	(send_environment_packet, extended_remote_environment_support)
	(extended_remote_set_inferior_cwd, remote_write_qxfer)
	(remote_read_qxfer, push_stop_reply, vcont_r_supported)
	(packet_command): Now methods of ...
	(remote_target): ... this class.
	(m_remote_state) <remote_target>: New field.
	(struct remote_state) <stop_reply_queue,
	remote_async_inferior_event_token, wait_forever_enabled_p>: New
	fields.
	(remote_state::remote_state): Allocate stop_reply_queue.
	(remote_state): Delete global.
	(get_remote_state_raw): Delete.
	(remote_target::get_remote_state): Allocate m_remote_state on
	demand.
	(get_current_remote_target): New.
	(remote_ops, extended_remote_ops): Delete.
	(wait_forever_enabled_p, remote_async_inferior_event_token):
	Delete, moved to struct remote_state.
	(remote_target::close): Delete self.  Destruction bits split to
	...
	(remote_target::~remote_target): ... this.
	(show_memory_packet_size): Adjust to use
	get_current_remote_target.
	(struct protocol_feature) <func>: Add remote_target parameter.
	All callers adjusted.
	(curr_quit_handler_target): New.
	(remote_serial_quit_handler): Reimplement.
	(remote_target::open_1): Adjust to use get_current_remote_target.
	Heap-allocate remote_target/extended_remote_target instances.
	(vcont_builder::vcont_builder): Add remote_target parameter, and
	save it in m_remote.  All callers adjusted.
	(vcont_builder::m_remote): New field.
	(vcont_builder::restart, vcont_builder::flush)
	(vcont_builder::push_action): Use it.
	(remote_target::commit_resume): Use it.
	(struct queue_iter_param) <remote>: New field.
	(remote_target::remove_new_fork_children): Fill in 'remote' field.
	(check_pending_event_prevents_wildcard_vcont_callback_data): New.
	(check_pending_event_prevents_wildcard_vcont_callback)
	(remote_target::check_pending_events_prevent_wildcard_vcont)
	(remote_target::discard_pending_stop_replies)
	(remote_target::discard_pending_stop_replies_in_queue)
	(remote_target::remote_notif_remove_queued_reply): Fill in
	'remote' field.
	(remote_notif_get_pending_events): New.
	(remote_target::readchar, remote_target::remote_serial_write):
	Save/restore curr_quit_handler_target.
	(putpkt): New.
	(kill_new_fork_children): Fill in 'remote' field.
	(packet_command): Use get_current_remote_target, defer to
	remote_target method of same name.
	(scoped_remote_fd::scoped_remote_fd): Add 'remote_target'
	parameter, and save it in m_remote.  All callers adjusted.
	(scoped_remote_fd::release): Use m_remote.
	(scoped_remote_fd::m_remote): New field.
	(remote_file_put, remote_file_get, remote_file_delete): Use
	get_current_remote_target, defer to remote_target method of same
	name.
	(remote_btrace_reset): Add remote_state paremeter.  Update all
	callers.
	(remote_async_inferior_event_handler). Pass down 'data'.
	(remote_new_objfile): Use get_current_remote_target.
	(remote_target::vcont_r_supported): New.
	(set_range_stepping): Use get_current_remote_target and
	remote_target::vcont_r_supported.
	(_initialize_remote): Don't allocate 'remote_state' and
	'stop_reply_queue' globals.
	* remote.h (struct remote_target): Forward declare.
	(getpkt, putpkt, remote_notif_get_pending_events): Add
	'remote_target' parameter.
2018-05-22 18:35:20 +01:00

1309 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Remote File-I/O communications
Copyright (C) 2003-2018 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/>. */
/* See the GDB User Guide for details of the GDB remote protocol. */
#include "defs.h"
#include "gdbcmd.h"
#include "remote.h"
#include "gdb_wait.h"
#include <sys/stat.h>
#include "remote-fileio.h"
#include "event-loop.h"
#include "target.h"
#include "filenames.h"
#include "filestuff.h"
#include <fcntl.h>
#include "gdb_sys_time.h"
#ifdef __CYGWIN__
#include <sys/cygwin.h> /* For cygwin_conv_path. */
#endif
#include <signal.h>
static struct {
int *fd_map;
int fd_map_size;
} remote_fio_data;
#define FIO_FD_INVALID -1
#define FIO_FD_CONSOLE_IN -2
#define FIO_FD_CONSOLE_OUT -3
static int remote_fio_system_call_allowed = 0;
static int
remote_fileio_init_fd_map (void)
{
int i;
if (!remote_fio_data.fd_map)
{
remote_fio_data.fd_map = XNEWVEC (int, 10);
remote_fio_data.fd_map_size = 10;
remote_fio_data.fd_map[0] = FIO_FD_CONSOLE_IN;
remote_fio_data.fd_map[1] = FIO_FD_CONSOLE_OUT;
remote_fio_data.fd_map[2] = FIO_FD_CONSOLE_OUT;
for (i = 3; i < 10; ++i)
remote_fio_data.fd_map[i] = FIO_FD_INVALID;
}
return 3;
}
static int
remote_fileio_resize_fd_map (void)
{
int i = remote_fio_data.fd_map_size;
if (!remote_fio_data.fd_map)
return remote_fileio_init_fd_map ();
remote_fio_data.fd_map_size += 10;
remote_fio_data.fd_map =
(int *) xrealloc (remote_fio_data.fd_map,
remote_fio_data.fd_map_size * sizeof (int));
for (; i < remote_fio_data.fd_map_size; i++)
remote_fio_data.fd_map[i] = FIO_FD_INVALID;
return remote_fio_data.fd_map_size - 10;
}
static int
remote_fileio_next_free_fd (void)
{
int i;
for (i = 0; i < remote_fio_data.fd_map_size; ++i)
if (remote_fio_data.fd_map[i] == FIO_FD_INVALID)
return i;
return remote_fileio_resize_fd_map ();
}
static int
remote_fileio_fd_to_targetfd (int fd)
{
int target_fd = remote_fileio_next_free_fd ();
remote_fio_data.fd_map[target_fd] = fd;
return target_fd;
}
static int
remote_fileio_map_fd (int target_fd)
{
remote_fileio_init_fd_map ();
if (target_fd < 0 || target_fd >= remote_fio_data.fd_map_size)
return FIO_FD_INVALID;
return remote_fio_data.fd_map[target_fd];
}
static void
remote_fileio_close_target_fd (int target_fd)
{
remote_fileio_init_fd_map ();
if (target_fd >= 0 && target_fd < remote_fio_data.fd_map_size)
remote_fio_data.fd_map[target_fd] = FIO_FD_INVALID;
}
static int
remote_fileio_oflags_to_host (long flags)
{
int hflags = 0;
if (flags & FILEIO_O_CREAT)
hflags |= O_CREAT;
if (flags & FILEIO_O_EXCL)
hflags |= O_EXCL;
if (flags & FILEIO_O_TRUNC)
hflags |= O_TRUNC;
if (flags & FILEIO_O_APPEND)
hflags |= O_APPEND;
if (flags & FILEIO_O_RDONLY)
hflags |= O_RDONLY;
if (flags & FILEIO_O_WRONLY)
hflags |= O_WRONLY;
if (flags & FILEIO_O_RDWR)
hflags |= O_RDWR;
/* On systems supporting binary and text mode, always open files in
binary mode. */
#ifdef O_BINARY
hflags |= O_BINARY;
#endif
return hflags;
}
static mode_t
remote_fileio_mode_to_host (long mode, int open_call)
{
mode_t hmode = 0;
if (!open_call)
{
if (mode & FILEIO_S_IFREG)
hmode |= S_IFREG;
if (mode & FILEIO_S_IFDIR)
hmode |= S_IFDIR;
if (mode & FILEIO_S_IFCHR)
hmode |= S_IFCHR;
}
if (mode & FILEIO_S_IRUSR)
hmode |= S_IRUSR;
if (mode & FILEIO_S_IWUSR)
hmode |= S_IWUSR;
if (mode & FILEIO_S_IXUSR)
hmode |= S_IXUSR;
#ifdef S_IRGRP
if (mode & FILEIO_S_IRGRP)
hmode |= S_IRGRP;
#endif
#ifdef S_IWGRP
if (mode & FILEIO_S_IWGRP)
hmode |= S_IWGRP;
#endif
#ifdef S_IXGRP
if (mode & FILEIO_S_IXGRP)
hmode |= S_IXGRP;
#endif
if (mode & FILEIO_S_IROTH)
hmode |= S_IROTH;
#ifdef S_IWOTH
if (mode & FILEIO_S_IWOTH)
hmode |= S_IWOTH;
#endif
#ifdef S_IXOTH
if (mode & FILEIO_S_IXOTH)
hmode |= S_IXOTH;
#endif
return hmode;
}
static int
remote_fileio_seek_flag_to_host (long num, int *flag)
{
if (!flag)
return 0;
switch (num)
{
case FILEIO_SEEK_SET:
*flag = SEEK_SET;
break;
case FILEIO_SEEK_CUR:
*flag = SEEK_CUR;
break;
case FILEIO_SEEK_END:
*flag = SEEK_END;
break;
default:
return -1;
}
return 0;
}
static int
remote_fileio_extract_long (char **buf, LONGEST *retlong)
{
char *c;
int sign = 1;
if (!buf || !*buf || !**buf || !retlong)
return -1;
c = strchr (*buf, ',');
if (c)
*c++ = '\0';
else
c = strchr (*buf, '\0');
while (strchr ("+-", **buf))
{
if (**buf == '-')
sign = -sign;
++*buf;
}
for (*retlong = 0; **buf; ++*buf)
{
*retlong <<= 4;
if (**buf >= '0' && **buf <= '9')
*retlong += **buf - '0';
else if (**buf >= 'a' && **buf <= 'f')
*retlong += **buf - 'a' + 10;
else if (**buf >= 'A' && **buf <= 'F')
*retlong += **buf - 'A' + 10;
else
return -1;
}
*retlong *= sign;
*buf = c;
return 0;
}
static int
remote_fileio_extract_int (char **buf, long *retint)
{
int ret;
LONGEST retlong;
if (!retint)
return -1;
ret = remote_fileio_extract_long (buf, &retlong);
if (!ret)
*retint = (long) retlong;
return ret;
}
static int
remote_fileio_extract_ptr_w_len (char **buf, CORE_ADDR *ptrval, int *length)
{
char *c;
LONGEST retlong;
if (!buf || !*buf || !**buf || !ptrval || !length)
return -1;
c = strchr (*buf, '/');
if (!c)
return -1;
*c++ = '\0';
if (remote_fileio_extract_long (buf, &retlong))
return -1;
*ptrval = (CORE_ADDR) retlong;
*buf = c;
if (remote_fileio_extract_long (buf, &retlong))
return -1;
*length = (int) retlong;
return 0;
}
static void
remote_fileio_to_fio_long (LONGEST num, fio_long_t fnum)
{
host_to_bigendian (num, (char *) fnum, 8);
}
static void
remote_fileio_to_fio_timeval (struct timeval *tv, struct fio_timeval *ftv)
{
host_to_fileio_time (tv->tv_sec, ftv->ftv_sec);
remote_fileio_to_fio_long (tv->tv_usec, ftv->ftv_usec);
}
/* The quit handler originally installed. */
static quit_handler_ftype *remote_fileio_o_quit_handler;
/* What to do on a QUIT call while handling a file I/O request. We
throw a quit exception, which is caught by remote_fileio_request
and translated to an EINTR reply back to the target. */
static void
remote_fileio_quit_handler (void)
{
if (check_quit_flag ())
quit ();
}
static void
remote_fileio_reply (remote_target *remote, int retcode, int error)
{
char buf[32];
int ctrl_c = check_quit_flag ();
strcpy (buf, "F");
if (retcode < 0)
{
strcat (buf, "-");
retcode = -retcode;
}
sprintf (buf + strlen (buf), "%x", retcode);
if (error || ctrl_c)
{
if (error && ctrl_c)
error = FILEIO_EINTR;
if (error < 0)
{
strcat (buf, "-");
error = -error;
}
sprintf (buf + strlen (buf), ",%x", error);
if (ctrl_c)
strcat (buf, ",C");
}
quit_handler = remote_fileio_o_quit_handler;
putpkt (remote, buf);
}
static void
remote_fileio_ioerror (remote_target *remote)
{
remote_fileio_reply (remote, -1, FILEIO_EIO);
}
static void
remote_fileio_badfd (remote_target *remote)
{
remote_fileio_reply (remote, -1, FILEIO_EBADF);
}
static void
remote_fileio_return_errno (remote_target *remote, int retcode)
{
remote_fileio_reply (remote, retcode, retcode < 0
? host_to_fileio_error (errno) : 0);
}
static void
remote_fileio_return_success (remote_target *remote, int retcode)
{
remote_fileio_reply (remote, retcode, 0);
}
static void
remote_fileio_func_open (remote_target *remote, char *buf)
{
CORE_ADDR ptrval;
int length;
long num;
int flags, fd;
mode_t mode;
char *pathname;
struct stat st;
/* 1. Parameter: Ptr to pathname / length incl. trailing zero. */
if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length))
{
remote_fileio_ioerror (remote);
return;
}
/* 2. Parameter: open flags */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
flags = remote_fileio_oflags_to_host (num);
/* 3. Parameter: open mode */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
mode = remote_fileio_mode_to_host (num, 1);
/* Request pathname. */
pathname = (char *) alloca (length);
if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
{
remote_fileio_ioerror (remote);
return;
}
/* Check if pathname exists and is not a regular file or directory. If so,
return an appropriate error code. Same for trying to open directories
for writing. */
if (!stat (pathname, &st))
{
if (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
{
remote_fileio_reply (remote, -1, FILEIO_ENODEV);
return;
}
if (S_ISDIR (st.st_mode)
&& ((flags & O_WRONLY) == O_WRONLY || (flags & O_RDWR) == O_RDWR))
{
remote_fileio_reply (remote, -1, FILEIO_EISDIR);
return;
}
}
fd = gdb_open_cloexec (pathname, flags, mode);
if (fd < 0)
{
remote_fileio_return_errno (remote, -1);
return;
}
fd = remote_fileio_fd_to_targetfd (fd);
remote_fileio_return_success (remote, fd);
}
static void
remote_fileio_func_close (remote_target *remote, char *buf)
{
long num;
int fd;
/* Parameter: file descriptor */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
fd = remote_fileio_map_fd ((int) num);
if (fd == FIO_FD_INVALID)
{
remote_fileio_badfd (remote);
return;
}
if (fd != FIO_FD_CONSOLE_IN && fd != FIO_FD_CONSOLE_OUT && close (fd))
remote_fileio_return_errno (remote, -1);
remote_fileio_close_target_fd ((int) num);
remote_fileio_return_success (remote, 0);
}
static void
remote_fileio_func_read (remote_target *remote, char *buf)
{
long target_fd, num;
LONGEST lnum;
CORE_ADDR ptrval;
int fd, ret;
gdb_byte *buffer;
size_t length;
off_t old_offset, new_offset;
/* 1. Parameter: file descriptor */
if (remote_fileio_extract_int (&buf, &target_fd))
{
remote_fileio_ioerror (remote);
return;
}
fd = remote_fileio_map_fd ((int) target_fd);
if (fd == FIO_FD_INVALID)
{
remote_fileio_badfd (remote);
return;
}
/* 2. Parameter: buffer pointer */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
ptrval = (CORE_ADDR) lnum;
/* 3. Parameter: buffer length */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
length = (size_t) num;
switch (fd)
{
case FIO_FD_CONSOLE_OUT:
remote_fileio_badfd (remote);
return;
case FIO_FD_CONSOLE_IN:
{
static char *remaining_buf = NULL;
static int remaining_length = 0;
buffer = (gdb_byte *) xmalloc (16384);
if (remaining_buf)
{
if (remaining_length > length)
{
memcpy (buffer, remaining_buf, length);
memmove (remaining_buf, remaining_buf + length,
remaining_length - length);
remaining_length -= length;
ret = length;
}
else
{
memcpy (buffer, remaining_buf, remaining_length);
xfree (remaining_buf);
remaining_buf = NULL;
ret = remaining_length;
}
}
else
{
/* Windows (at least XP and Server 2003) has difficulty
with large reads from consoles. If a handle is
backed by a real console device, overly large reads
from the handle will fail and set errno == ENOMEM.
On a Windows Server 2003 system where I tested,
reading 26608 bytes from the console was OK, but
anything above 26609 bytes would fail. The limit has
been observed to vary on different systems. So, we
limit this read to something smaller than that - by a
safe margin, in case the limit depends on system
resources or version. */
ret = ui_file_read (gdb_stdtargin, (char *) buffer, 16383);
if (ret > 0 && (size_t)ret > length)
{
remaining_buf = (char *) xmalloc (ret - length);
remaining_length = ret - length;
memcpy (remaining_buf, buffer + length, remaining_length);
ret = length;
}
}
}
break;
default:
buffer = (gdb_byte *) xmalloc (length);
/* POSIX defines EINTR behaviour of read in a weird way. It's allowed
for read() to return -1 even if "some" bytes have been read. It
has been corrected in SUSv2 but that doesn't help us much...
Therefore a complete solution must check how many bytes have been
read on EINTR to return a more reliable value to the target */
old_offset = lseek (fd, 0, SEEK_CUR);
ret = read (fd, buffer, length);
if (ret < 0 && errno == EINTR)
{
new_offset = lseek (fd, 0, SEEK_CUR);
/* If some data has been read, return the number of bytes read.
The Ctrl-C flag is set in remote_fileio_reply() anyway. */
if (old_offset != new_offset)
ret = new_offset - old_offset;
}
break;
}
if (ret > 0)
{
errno = target_write_memory (ptrval, buffer, ret);
if (errno != 0)
ret = -1;
}
if (ret < 0)
remote_fileio_return_errno (remote, -1);
else
remote_fileio_return_success (remote, ret);
xfree (buffer);
}
static void
remote_fileio_func_write (remote_target *remote, char *buf)
{
long target_fd, num;
LONGEST lnum;
CORE_ADDR ptrval;
int fd, ret;
gdb_byte *buffer;
size_t length;
/* 1. Parameter: file descriptor */
if (remote_fileio_extract_int (&buf, &target_fd))
{
remote_fileio_ioerror (remote);
return;
}
fd = remote_fileio_map_fd ((int) target_fd);
if (fd == FIO_FD_INVALID)
{
remote_fileio_badfd (remote);
return;
}
/* 2. Parameter: buffer pointer */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
ptrval = (CORE_ADDR) lnum;
/* 3. Parameter: buffer length */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
length = (size_t) num;
buffer = (gdb_byte *) xmalloc (length);
if (target_read_memory (ptrval, buffer, length) != 0)
{
xfree (buffer);
remote_fileio_ioerror (remote);
return;
}
switch (fd)
{
case FIO_FD_CONSOLE_IN:
remote_fileio_badfd (remote);
xfree (buffer);
return;
case FIO_FD_CONSOLE_OUT:
ui_file_write (target_fd == 1 ? gdb_stdtarg : gdb_stdtargerr,
(char *) buffer, length);
gdb_flush (target_fd == 1 ? gdb_stdtarg : gdb_stdtargerr);
ret = length;
break;
default:
ret = write (fd, buffer, length);
if (ret < 0 && errno == EACCES)
errno = EBADF; /* Cygwin returns EACCESS when writing to a
R/O file. */
break;
}
if (ret < 0)
remote_fileio_return_errno (remote, -1);
else
remote_fileio_return_success (remote, ret);
xfree (buffer);
}
static void
remote_fileio_func_lseek (remote_target *remote, char *buf)
{
long num;
LONGEST lnum;
int fd, flag;
off_t offset, ret;
/* 1. Parameter: file descriptor */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
fd = remote_fileio_map_fd ((int) num);
if (fd == FIO_FD_INVALID)
{
remote_fileio_badfd (remote);
return;
}
else if (fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT)
{
remote_fileio_reply (remote, -1, FILEIO_ESPIPE);
return;
}
/* 2. Parameter: offset */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
offset = (off_t) lnum;
/* 3. Parameter: flag */
if (remote_fileio_extract_int (&buf, &num))
{
remote_fileio_ioerror (remote);
return;
}
if (remote_fileio_seek_flag_to_host (num, &flag))
{
remote_fileio_reply (remote, -1, FILEIO_EINVAL);
return;
}
ret = lseek (fd, offset, flag);
if (ret == (off_t) -1)
remote_fileio_return_errno (remote, -1);
else
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_rename (remote_target *remote, char *buf)
{
CORE_ADDR old_ptr, new_ptr;
int old_len, new_len;
char *oldpath, *newpath;
int ret, of, nf;
struct stat ost, nst;
/* 1. Parameter: Ptr to oldpath / length incl. trailing zero */
if (remote_fileio_extract_ptr_w_len (&buf, &old_ptr, &old_len))
{
remote_fileio_ioerror (remote);
return;
}
/* 2. Parameter: Ptr to newpath / length incl. trailing zero */
if (remote_fileio_extract_ptr_w_len (&buf, &new_ptr, &new_len))
{
remote_fileio_ioerror (remote);
return;
}
/* Request oldpath using 'm' packet */
oldpath = (char *) alloca (old_len);
if (target_read_memory (old_ptr, (gdb_byte *) oldpath, old_len) != 0)
{
remote_fileio_ioerror (remote);
return;
}
/* Request newpath using 'm' packet */
newpath = (char *) alloca (new_len);
if (target_read_memory (new_ptr, (gdb_byte *) newpath, new_len) != 0)
{
remote_fileio_ioerror (remote);
return;
}
/* Only operate on regular files and directories. */
of = stat (oldpath, &ost);
nf = stat (newpath, &nst);
if ((!of && !S_ISREG (ost.st_mode) && !S_ISDIR (ost.st_mode))
|| (!nf && !S_ISREG (nst.st_mode) && !S_ISDIR (nst.st_mode)))
{
remote_fileio_reply (remote, -1, FILEIO_EACCES);
return;
}
ret = rename (oldpath, newpath);
if (ret == -1)
{
/* Special case: newpath is a non-empty directory. Some systems
return ENOTEMPTY, some return EEXIST. We coerce that to be
always EEXIST. */
if (errno == ENOTEMPTY)
errno = EEXIST;
#ifdef __CYGWIN__
/* Workaround some Cygwin problems with correct errnos. */
if (errno == EACCES)
{
if (!of && !nf && S_ISDIR (nst.st_mode))
{
if (S_ISREG (ost.st_mode))
errno = EISDIR;
else
{
char oldfullpath[PATH_MAX];
char newfullpath[PATH_MAX];
int len;
cygwin_conv_path (CCP_WIN_A_TO_POSIX, oldpath, oldfullpath,
PATH_MAX);
cygwin_conv_path (CCP_WIN_A_TO_POSIX, newpath, newfullpath,
PATH_MAX);
len = strlen (oldfullpath);
if (IS_DIR_SEPARATOR (newfullpath[len])
&& !filename_ncmp (oldfullpath, newfullpath, len))
errno = EINVAL;
else
errno = EEXIST;
}
}
}
#endif
remote_fileio_return_errno (remote, -1);
}
else
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_unlink (remote_target *remote, char *buf)
{
CORE_ADDR ptrval;
int length;
char *pathname;
int ret;
struct stat st;
/* Parameter: Ptr to pathname / length incl. trailing zero */
if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length))
{
remote_fileio_ioerror (remote);
return;
}
/* Request pathname using 'm' packet */
pathname = (char *) alloca (length);
if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
{
remote_fileio_ioerror (remote);
return;
}
/* Only operate on regular files (and directories, which allows to return
the correct return code). */
if (!stat (pathname, &st) && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
{
remote_fileio_reply (remote, -1, FILEIO_ENODEV);
return;
}
ret = unlink (pathname);
if (ret == -1)
remote_fileio_return_errno (remote, -1);
else
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_stat (remote_target *remote, char *buf)
{
CORE_ADDR statptr, nameptr;
int ret, namelength;
char *pathname;
LONGEST lnum;
struct stat st;
struct fio_stat fst;
/* 1. Parameter: Ptr to pathname / length incl. trailing zero */
if (remote_fileio_extract_ptr_w_len (&buf, &nameptr, &namelength))
{
remote_fileio_ioerror (remote);
return;
}
/* 2. Parameter: Ptr to struct stat */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
statptr = (CORE_ADDR) lnum;
/* Request pathname using 'm' packet */
pathname = (char *) alloca (namelength);
if (target_read_memory (nameptr, (gdb_byte *) pathname, namelength) != 0)
{
remote_fileio_ioerror (remote);
return;
}
ret = stat (pathname, &st);
if (ret == -1)
{
remote_fileio_return_errno (remote, -1);
return;
}
/* Only operate on regular files and directories. */
if (!ret && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode))
{
remote_fileio_reply (remote, -1, FILEIO_EACCES);
return;
}
if (statptr)
{
host_to_fileio_stat (&st, &fst);
host_to_fileio_uint (0, fst.fst_dev);
errno = target_write_memory (statptr, (gdb_byte *) &fst, sizeof fst);
if (errno != 0)
{
remote_fileio_return_errno (remote, -1);
return;
}
}
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_fstat (remote_target *remote, char *buf)
{
CORE_ADDR ptrval;
int fd, ret;
long target_fd;
LONGEST lnum;
struct stat st;
struct fio_stat fst;
struct timeval tv;
/* 1. Parameter: file descriptor */
if (remote_fileio_extract_int (&buf, &target_fd))
{
remote_fileio_ioerror (remote);
return;
}
fd = remote_fileio_map_fd ((int) target_fd);
if (fd == FIO_FD_INVALID)
{
remote_fileio_badfd (remote);
return;
}
/* 2. Parameter: Ptr to struct stat */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
ptrval = (CORE_ADDR) lnum;
if (fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT)
{
host_to_fileio_uint (1, fst.fst_dev);
memset (&st, 0, sizeof (st));
st.st_mode = S_IFCHR | (fd == FIO_FD_CONSOLE_IN ? S_IRUSR : S_IWUSR);
st.st_nlink = 1;
#ifdef HAVE_GETUID
st.st_uid = getuid ();
#endif
#ifdef HAVE_GETGID
st.st_gid = getgid ();
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
st.st_blksize = 512;
#endif
#if HAVE_STRUCT_STAT_ST_BLOCKS
st.st_blocks = 0;
#endif
if (!gettimeofday (&tv, NULL))
st.st_atime = st.st_mtime = st.st_ctime = tv.tv_sec;
else
st.st_atime = st.st_mtime = st.st_ctime = (time_t) 0;
ret = 0;
}
else
ret = fstat (fd, &st);
if (ret == -1)
{
remote_fileio_return_errno (remote, -1);
return;
}
if (ptrval)
{
host_to_fileio_stat (&st, &fst);
errno = target_write_memory (ptrval, (gdb_byte *) &fst, sizeof fst);
if (errno != 0)
{
remote_fileio_return_errno (remote, -1);
return;
}
}
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_gettimeofday (remote_target *remote, char *buf)
{
LONGEST lnum;
CORE_ADDR ptrval;
int ret;
struct timeval tv;
struct fio_timeval ftv;
/* 1. Parameter: struct timeval pointer */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
ptrval = (CORE_ADDR) lnum;
/* 2. Parameter: some pointer value... */
if (remote_fileio_extract_long (&buf, &lnum))
{
remote_fileio_ioerror (remote);
return;
}
/* ...which has to be NULL. */
if (lnum)
{
remote_fileio_reply (remote, -1, FILEIO_EINVAL);
return;
}
ret = gettimeofday (&tv, NULL);
if (ret == -1)
{
remote_fileio_return_errno (remote, -1);
return;
}
if (ptrval)
{
remote_fileio_to_fio_timeval (&tv, &ftv);
errno = target_write_memory (ptrval, (gdb_byte *) &ftv, sizeof ftv);
if (errno != 0)
{
remote_fileio_return_errno (remote, -1);
return;
}
}
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_isatty (remote_target *remote, char *buf)
{
long target_fd;
int fd;
/* Parameter: file descriptor */
if (remote_fileio_extract_int (&buf, &target_fd))
{
remote_fileio_ioerror (remote);
return;
}
fd = remote_fileio_map_fd ((int) target_fd);
int ret = fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT ? 1 : 0;
remote_fileio_return_success (remote, ret);
}
static void
remote_fileio_func_system (remote_target *remote, char *buf)
{
CORE_ADDR ptrval;
int ret, length;
char *cmdline = NULL;
/* Parameter: Ptr to commandline / length incl. trailing zero */
if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length))
{
remote_fileio_ioerror (remote);
return;
}
if (length)
{
/* Request commandline using 'm' packet */
cmdline = (char *) alloca (length);
if (target_read_memory (ptrval, (gdb_byte *) cmdline, length) != 0)
{
remote_fileio_ioerror (remote);
return;
}
}
/* Check if system(3) has been explicitely allowed using the
`set remote system-call-allowed 1' command. If length is 0,
indicating a NULL parameter to the system call, return zero to
indicate a shell is not available. Otherwise fail with EPERM. */
if (!remote_fio_system_call_allowed)
{
if (!length)
remote_fileio_return_success (remote, 0);
else
remote_fileio_reply (remote, -1, FILEIO_EPERM);
return;
}
ret = system (cmdline);
if (!length)
remote_fileio_return_success (remote, ret);
else if (ret == -1)
remote_fileio_return_errno (remote, -1);
else
remote_fileio_return_success (remote, WEXITSTATUS (ret));
}
static struct {
const char *name;
void (*func)(remote_target *remote, char *);
} remote_fio_func_map[] = {
{ "open", remote_fileio_func_open },
{ "close", remote_fileio_func_close },
{ "read", remote_fileio_func_read },
{ "write", remote_fileio_func_write },
{ "lseek", remote_fileio_func_lseek },
{ "rename", remote_fileio_func_rename },
{ "unlink", remote_fileio_func_unlink },
{ "stat", remote_fileio_func_stat },
{ "fstat", remote_fileio_func_fstat },
{ "gettimeofday", remote_fileio_func_gettimeofday },
{ "isatty", remote_fileio_func_isatty },
{ "system", remote_fileio_func_system },
{ NULL, NULL }
};
static void
do_remote_fileio_request (remote_target *remote, char *buf)
{
char *c;
int idx;
quit_handler = remote_fileio_quit_handler;
c = strchr (++buf, ',');
if (c)
*c++ = '\0';
else
c = strchr (buf, '\0');
for (idx = 0; remote_fio_func_map[idx].name; ++idx)
if (!strcmp (remote_fio_func_map[idx].name, buf))
break;
if (!remote_fio_func_map[idx].name)
remote_fileio_reply (remote, -1, FILEIO_ENOSYS);
else
remote_fio_func_map[idx].func (remote, c);
}
/* Close any open descriptors, and reinitialize the file mapping. */
void
remote_fileio_reset (void)
{
int ix;
for (ix = 0; ix != remote_fio_data.fd_map_size; ix++)
{
int fd = remote_fio_data.fd_map[ix];
if (fd >= 0)
close (fd);
}
if (remote_fio_data.fd_map)
{
xfree (remote_fio_data.fd_map);
remote_fio_data.fd_map = NULL;
remote_fio_data.fd_map_size = 0;
}
}
/* Handle a file I/O request. BUF points to the packet containing the
request. CTRLC_PENDING_P should be nonzero if the target has not
acknowledged the Ctrl-C sent asynchronously earlier. */
void
remote_fileio_request (remote_target *remote, char *buf, int ctrlc_pending_p)
{
/* Save the previous quit handler, so we can restore it. No need
for a cleanup since we catch all exceptions below. Note that the
quit handler is also restored by remote_fileio_reply just before
pushing a packet. */
remote_fileio_o_quit_handler = quit_handler;
if (ctrlc_pending_p)
{
/* If the target hasn't responded to the Ctrl-C sent
asynchronously earlier, take this opportunity to send the
Ctrl-C synchronously. */
set_quit_flag ();
remote_fileio_reply (remote, -1, FILEIO_EINTR);
}
else
{
TRY
{
do_remote_fileio_request (remote, buf);
}
CATCH (ex, RETURN_MASK_ALL)
{
if (ex.reason == RETURN_QUIT)
remote_fileio_reply (remote, -1, FILEIO_EINTR);
else
remote_fileio_reply (remote, -1, FILEIO_EIO);
}
END_CATCH
}
quit_handler = remote_fileio_o_quit_handler;
}
/* Unpack an fio_uint_t. */
static unsigned int
remote_fileio_to_host_uint (fio_uint_t fnum)
{
return extract_unsigned_integer ((gdb_byte *) fnum, 4,
BFD_ENDIAN_BIG);
}
/* Unpack an fio_ulong_t. */
static ULONGEST
remote_fileio_to_host_ulong (fio_ulong_t fnum)
{
return extract_unsigned_integer ((gdb_byte *) fnum, 8,
BFD_ENDIAN_BIG);
}
/* Unpack an fio_mode_t. */
static mode_t
remote_fileio_to_host_mode (fio_mode_t fnum)
{
return remote_fileio_mode_to_host (remote_fileio_to_host_uint (fnum),
0);
}
/* Unpack an fio_time_t. */
static time_t
remote_fileio_to_host_time (fio_time_t fnum)
{
return remote_fileio_to_host_uint (fnum);
}
/* See remote-fileio.h. */
void
remote_fileio_to_host_stat (struct fio_stat *fst, struct stat *st)
{
memset (st, 0, sizeof (struct stat));
st->st_dev = remote_fileio_to_host_uint (fst->fst_dev);
st->st_ino = remote_fileio_to_host_uint (fst->fst_ino);
st->st_mode = remote_fileio_to_host_mode (fst->fst_mode);
st->st_nlink = remote_fileio_to_host_uint (fst->fst_nlink);
st->st_uid = remote_fileio_to_host_uint (fst->fst_uid);
st->st_gid = remote_fileio_to_host_uint (fst->fst_gid);
st->st_rdev = remote_fileio_to_host_uint (fst->fst_rdev);
st->st_size = remote_fileio_to_host_ulong (fst->fst_size);
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
st->st_blksize = remote_fileio_to_host_ulong (fst->fst_blksize);
#endif
#if HAVE_STRUCT_STAT_ST_BLOCKS
st->st_blocks = remote_fileio_to_host_ulong (fst->fst_blocks);
#endif
st->st_atime = remote_fileio_to_host_time (fst->fst_atime);
st->st_mtime = remote_fileio_to_host_time (fst->fst_mtime);
st->st_ctime = remote_fileio_to_host_time (fst->fst_ctime);
}
static void
set_system_call_allowed (const char *args, int from_tty)
{
if (args)
{
char *arg_end;
int val = strtoul (args, &arg_end, 10);
if (*args && *arg_end == '\0')
{
remote_fio_system_call_allowed = !!val;
return;
}
}
error (_("Illegal argument for \"set remote system-call-allowed\" command"));
}
static void
show_system_call_allowed (const char *args, int from_tty)
{
if (args)
error (_("Garbage after \"show remote "
"system-call-allowed\" command: `%s'"), args);
printf_unfiltered ("Calling host system(3) call from target is %sallowed\n",
remote_fio_system_call_allowed ? "" : "not ");
}
void
initialize_remote_fileio (struct cmd_list_element *remote_set_cmdlist,
struct cmd_list_element *remote_show_cmdlist)
{
add_cmd ("system-call-allowed", no_class,
set_system_call_allowed,
_("Set if the host system(3) call is allowed for the target."),
&remote_set_cmdlist);
add_cmd ("system-call-allowed", no_class,
show_system_call_allowed,
_("Show if the host system(3) call is allowed for the target."),
&remote_show_cmdlist);
}