text: /*
   * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl>
   * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl>
   * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com>
   * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl>
   * Copyright (c) 1999-2018 The strace developers.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. The name of the author may not be used to endorse or promote products
   *    derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "defs.h"
  #include <fcntl.h>
  #include <sys/uio.h>
  
  SYS_FUNC(read)
  {
  	if (entering(tcp)) {
  		printfd(tcp, tcp->u_arg[0]);
  		tprints(", ");
  	} else {
  		if (syserror(tcp))
  			printaddr(tcp->u_arg[1]);
  		else
  			printstrn(tcp, tcp->u_arg[1], tcp->u_rval);
  		tprintf(", %" PRI_klu, tcp->u_arg[2]);
  	}
  	return 0;
  }
  
  SYS_FUNC(write)
  {
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	printstrn(tcp, tcp->u_arg[1], tcp->u_arg[2]);
  	tprintf(", %" PRI_klu, tcp->u_arg[2]);
  
  	return RVAL_DECODED;
  }
  
  struct print_iovec_config {
  	enum iov_decode decode_iov;
  	kernel_ulong_t data_size;
  };
  
  static bool
  print_iovec(struct tcb *tcp, void *elem_buf, size_t elem_size, void *data)
  {
  	const kernel_ulong_t *iov;
  	kernel_ulong_t iov_buf[2], len;
  	struct print_iovec_config *c = data;
  
  	if (elem_size < sizeof(iov_buf)) {
  		iov_buf[0] = ((unsigned int *) elem_buf)[0];
  		iov_buf[1] = ((unsigned int *) elem_buf)[1];
  		iov = iov_buf;
  	} else {
  		iov = elem_buf;
  	}
  
  	tprints("{iov_base=");
  
  	len = iov[1];
  
  	switch (c->decode_iov) {
  		case IOV_DECODE_STR:
  			if (len > c->data_size)
  				len = c->data_size;
  			if (c->data_size != (kernel_ulong_t) -1)
  				c->data_size -= len;
  			printstrn(tcp, iov[0], len);
  			break;
  		case IOV_DECODE_NETLINK:
  			if (len > c->data_size)
  				len = c->data_size;
  			if (c->data_size != (kernel_ulong_t) -1)
  				c->data_size -= len;
  			/* assume that the descriptor is 1st syscall argument */
  			decode_netlink(tcp, tcp->u_arg[0], iov[0], len);
  			break;
  		default:
  			printaddr(iov[0]);
  			break;
  	}
  
  	tprintf(", iov_len=%" PRI_klu "}", iov[1]);
  
  	return true;
  }
  
  /*
   * data_size limits the cumulative size of printed data.
   * Example: recvmsg returing a short read.
   */
  void
  tprint_iov_upto(struct tcb *const tcp, const kernel_ulong_t len,
  		const kernel_ulong_t addr, const enum iov_decode decode_iov,
  		const kernel_ulong_t data_size)
  {
  	kernel_ulong_t iov[2];
  	struct print_iovec_config config = {
  		.decode_iov = decode_iov, .data_size = data_size
  	};
  
  	print_array(tcp, addr, len, iov, current_wordsize * 2,
  		    tfetch_mem_ignore_syserror, print_iovec, &config);
  }
  
  SYS_FUNC(readv)
  {
  	if (entering(tcp)) {
  		printfd(tcp, tcp->u_arg[0]);
  		tprints(", ");
  	} else {
  		tprint_iov_upto(tcp, tcp->u_arg[2], tcp->u_arg[1],
  				syserror(tcp) ? IOV_DECODE_ADDR :
  				IOV_DECODE_STR, tcp->u_rval);
  		tprintf(", %" PRI_klu, tcp->u_arg[2]);
  	}
  	return 0;
  }
  
  SYS_FUNC(writev)
  {
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	tprint_iov(tcp, tcp->u_arg[2], tcp->u_arg[1], IOV_DECODE_STR);
  	tprintf(", %" PRI_klu, tcp->u_arg[2]);
  
  	return RVAL_DECODED;
  }
  
  SYS_FUNC(pread)
  {
  	if (entering(tcp)) {
  		printfd(tcp, tcp->u_arg[0]);
  		tprints(", ");
  	} else {
  		if (syserror(tcp))
  			printaddr(tcp->u_arg[1]);
  		else
  			printstrn(tcp, tcp->u_arg[1], tcp->u_rval);
  		tprintf(", %" PRI_klu ", ", tcp->u_arg[2]);
  		printllval(tcp, "%lld", 3);
  	}
  	return 0;
  }
  
  SYS_FUNC(pwrite)
  {
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	printstrn(tcp, tcp->u_arg[1], tcp->u_arg[2]);
  	tprintf(", %" PRI_klu ", ", tcp->u_arg[2]);
  	printllval(tcp, "%lld", 3);
  
  	return RVAL_DECODED;
  }
  
  static void
  print_lld_from_low_high_val(struct tcb *tcp, int arg)
  {
  #if SIZEOF_KERNEL_LONG_T > 4
  # ifndef current_klongsize
  	if (current_klongsize < SIZEOF_KERNEL_LONG_T) {
  		tprintf("%" PRI_kld, (tcp->u_arg[arg + 1] << 32)
  			       | tcp->u_arg[arg]);
  	} else
  # endif /* !current_klongsize */
  	{
  		tprintf("%" PRI_kld, tcp->u_arg[arg]);
  	}
  #else /* SIZEOF_KERNEL_LONG_T == 4 */
  	tprintf("%lld",
  		  ((long long) tcp->u_arg[arg + 1] << 32)
  		| ((long long) tcp->u_arg[arg]));
  #endif
  }
  
  #include "xlat/rwf_flags.h"
  
  static int
  do_preadv(struct tcb *tcp, const int flags_arg)
  {
  	if (entering(tcp)) {
  		printfd(tcp, tcp->u_arg[0]);
  		tprints(", ");
  	} else {
  		kernel_ulong_t len =
  			truncate_kulong_to_current_wordsize(tcp->u_arg[2]);
  
  		tprint_iov_upto(tcp, len, tcp->u_arg[1],
  				syserror(tcp) ? IOV_DECODE_ADDR :
  				IOV_DECODE_STR, tcp->u_rval);
  		tprintf(", %" PRI_klu ", ", len);
  		print_lld_from_low_high_val(tcp, 3);
  		if (flags_arg >= 0) {
  			tprints(", ");
  			printflags(rwf_flags, tcp->u_arg[flags_arg], "RWF_???");
  		}
  	}
  	return 0;
  }
  
  SYS_FUNC(preadv)
  {
  	return do_preadv(tcp, -1);
  }
  
  static int
  do_pwritev(struct tcb *tcp, const int flags_arg)
  {
  	kernel_ulong_t len =
  		truncate_kulong_to_current_wordsize(tcp->u_arg[2]);
  
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	tprint_iov(tcp, len, tcp->u_arg[1], IOV_DECODE_STR);
  	tprintf(", %" PRI_klu ", ", len);
  	print_lld_from_low_high_val(tcp, 3);
  	if (flags_arg >= 0) {
  		tprints(", ");
  		printflags(rwf_flags, tcp->u_arg[flags_arg], "RWF_???");
  	}
  
  	return RVAL_DECODED;
  }
  
  SYS_FUNC(pwritev)
  {
  	return do_pwritev(tcp, -1);
  }
  
  /*
   * x32 is the only architecture where preadv2 takes 5 arguments
   * instead of 6, see preadv64v2 in kernel sources.
   * Likewise, x32 is the only architecture where pwritev2 takes 5 arguments
   * instead of 6, see pwritev64v2 in kernel sources.
   */
  
  #if defined X86_64
  # define PREADV2_PWRITEV2_FLAGS_ARG_NO (current_personality == 2 ? 4 : 5)
  #elif defined X32
  # define PREADV2_PWRITEV2_FLAGS_ARG_NO (current_personality == 0 ? 4 : 5)
  #else
  # define PREADV2_PWRITEV2_FLAGS_ARG_NO 5
  #endif
  
  SYS_FUNC(preadv2)
  {
  	return do_preadv(tcp, PREADV2_PWRITEV2_FLAGS_ARG_NO);
  }
  
  SYS_FUNC(pwritev2)
  {
  	return do_pwritev(tcp, PREADV2_PWRITEV2_FLAGS_ARG_NO);
  }
  
  #include "xlat/splice_flags.h"
  
  SYS_FUNC(tee)
  {
  	/* int fd_in */
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	/* int fd_out */
  	printfd(tcp, tcp->u_arg[1]);
  	tprints(", ");
  	/* size_t len */
  	tprintf("%" PRI_klu ", ", tcp->u_arg[2]);
  	/* unsigned int flags */
  	printflags(splice_flags, tcp->u_arg[3], "SPLICE_F_???");
  
  	return RVAL_DECODED;
  }
  
  SYS_FUNC(splice)
  {
  	/* int fd_in */
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	/* loff_t *off_in */
  	printnum_int64(tcp, tcp->u_arg[1], "%" PRId64);
  	tprints(", ");
  	/* int fd_out */
  	printfd(tcp, tcp->u_arg[2]);
  	tprints(", ");
  	/* loff_t *off_out */
  	printnum_int64(tcp, tcp->u_arg[3], "%" PRId64);
  	tprints(", ");
  	/* size_t len */
  	tprintf("%" PRI_klu ", ", tcp->u_arg[4]);
  	/* unsigned int flags */
  	printflags(splice_flags, tcp->u_arg[5], "SPLICE_F_???");
  
  	return RVAL_DECODED;
  }
  
  SYS_FUNC(vmsplice)
  {
  	/* int fd */
  	printfd(tcp, tcp->u_arg[0]);
  	tprints(", ");
  	/* const struct iovec *iov, unsigned long nr_segs */
  	tprint_iov(tcp, tcp->u_arg[2], tcp->u_arg[1], IOV_DECODE_STR);
  	tprintf(", %" PRI_klu ", ", tcp->u_arg[2]);
  	/* unsigned int flags */
  	printflags(splice_flags, tcp->u_arg[3], "SPLICE_F_???");
  
  	return RVAL_DECODED;
  }
  