1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <errno.h>
  30 #include <unistd.h>
  31 #include <sys/uio.h>
  32 #include <fcntl.h>
  33 #include <sys/types.h>
  34 #include <sys/stat.h>
  35 #include <alloca.h>
  36 #include <string.h>
  37 #include <sys/lx_syscall.h>
  38 #include <sys/lx_misc.h>
  39 #include <sys/lx_types.h>
  40 
  41 static int
  42 lx_is_directory(int fd)
  43 {
  44         struct stat64 sbuf;
  45 
  46         if (fstat64(fd, &sbuf) < 0)
  47                 sbuf.st_mode = 0;
  48 
  49         return ((sbuf.st_mode & S_IFMT) == S_IFDIR);
  50 }
  51 
  52 int
  53 lx_read(uintptr_t p1, uintptr_t p2, uintptr_t p3)
  54 {
  55         int             fd = (int)p1;
  56         void            *buf = (void *)p2;
  57         size_t          nbyte = (size_t)p3;
  58         ssize_t         ret;
  59 
  60         if (lx_is_directory(fd))
  61                 return (-EISDIR);
  62 
  63         if ((ret = read(fd, buf, nbyte)) < 0)
  64                 return (-errno);
  65 
  66         return (ret);
  67 }
  68 
  69 int
  70 lx_pread64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5)
  71 {
  72         int             fd = (int)p1;
  73         void            *buf = (void *)p2;
  74         size_t          nbyte = (size_t)p3;
  75         uintptr_t       off_lo = p4;
  76         uintptr_t       off_hi = p5;
  77         ssize_t         ret;
  78 
  79         if (lx_is_directory(fd))
  80                 return (-EISDIR);
  81 
  82         ret = pread64(fd, buf, nbyte, (off64_t)LX_32TO64(off_lo, off_hi));
  83 
  84         if (ret < 0)
  85                 return (-errno);
  86 
  87         return (ret);
  88 }
  89 
  90 /*
  91  * On Linux, the pwrite(2) system call behaves identically to Solaris except
  92  * in the case of the file being opened with O_APPEND. In that case Linux's
  93  * pwrite(2) ignores the offset parameter and instead appends the data to the
  94  * file without modifying the current seek pointer.
  95  */
  96 int
  97 lx_pwrite64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
  98     uintptr_t p5)
  99 {
 100         int fd = (int)p1;
 101         void *buf = (void *)p2;
 102         size_t nbyte = (size_t)p3;
 103         uintptr_t off_lo = p4;
 104         uintptr_t off_hi = p5;
 105         ssize_t ret;
 106         int rval;
 107         struct stat64 statbuf;
 108 
 109         if ((rval = fcntl(fd, F_GETFL, 0)) < 0)
 110                 return (-errno);
 111 
 112         if (!(rval & O_APPEND)) {
 113                 ret = pwrite64(fd, buf, nbyte,
 114                     (off64_t)LX_32TO64(off_lo, off_hi));
 115         } else if ((ret = fstat64(fd, &statbuf)) == 0) {
 116                 ret = pwrite64(fd, buf, nbyte, statbuf.st_size);
 117         }
 118 
 119         if (ret < 0)
 120                 return (-errno);
 121 
 122         return (ret);
 123 }
 124 
 125 /*
 126  * Implementation of Linux readv() and writev() system calls.
 127  *
 128  * The Linux system calls differ from the Solaris system calls in a few key
 129  * areas:
 130  *
 131  * - On Solaris, the maximum number of I/O vectors that can be passed to readv()
 132  *   or writev() is IOV_MAX (16).  Linux has a much larger restriction (1024).
 133  *
 134  * - Passing 0 as a vector count is an error on Solaris, but on Linux results
 135  *   in a return value of 0. Even though the man page says the opposite.
 136  *
 137  * - If the Nth vector results in an error, Solaris will return an error code
 138  *   for the entire operation.  Linux only returns an error if there has been
 139  *   no data transferred yet.  Otherwise, it returns the number of bytes
 140  *   transferred up until that point.
 141  *
 142  * In order to accomodate these differences, we implement these functions as a
 143  * series of ordinary read() or write() calls.
 144  */
 145 
 146 #define LX_IOV_MAX 1024         /* Also called MAX_IOVEC */
 147 
 148 static int
 149 lx_iovec_copy_and_check(const struct iovec *iovp, struct iovec *iov, int count)
 150 {
 151         int     i;
 152         ssize_t cnt = 0;
 153 
 154         if (uucopy(iovp, (void *)iov, count * sizeof (struct iovec)) != 0)
 155                 return (-errno);
 156 
 157         for (i = 0; i < count; i++) {
 158                 cnt += iov[i].iov_len;
 159                 if (iov[i].iov_len < 0 || cnt < 0)
 160                         return (-EINVAL);
 161         }
 162 
 163         return (0);
 164 }
 165 
 166 int
 167 lx_readv(uintptr_t p1, uintptr_t p2, uintptr_t p3)
 168 {
 169         int                     fd = (int)p1;
 170         const struct iovec      *iovp = (const struct iovec *)p2;
 171         int                     count = (int)p3;
 172         struct iovec            *iov;
 173         ssize_t                 total = 0, ret;
 174         int                     i;
 175 
 176         if (count == 0)
 177                 return (0);
 178 
 179         if (count < 0 || count > LX_IOV_MAX)
 180                 return (-EINVAL);
 181 
 182         if (lx_is_directory(fd))
 183                 return (-EISDIR);
 184 
 185         iov = SAFE_ALLOCA(count * sizeof (struct iovec));
 186         if (iov == NULL)
 187                 return (-ENOMEM);
 188         if ((ret = lx_iovec_copy_and_check(iovp, iov, count)) != 0)
 189                 return (ret);
 190 
 191         for (i = 0; i < count; i++) {
 192                 ret = read(fd, iov[i].iov_base, iov[i].iov_len);
 193 
 194                 if (ret < 0) {
 195                         if (total > 0)
 196                                 return (total);
 197                         return (-errno);
 198                 }
 199 
 200                 total += ret;
 201         }
 202 
 203         return (total);
 204 }
 205 
 206 int
 207 lx_writev(uintptr_t p1, uintptr_t p2, uintptr_t p3)
 208 {
 209         int                     fd = (int)p1;
 210         const struct iovec      *iovp = (const struct iovec *)p2;
 211         int                     count = (int)p3;
 212         struct iovec            *iov;
 213         ssize_t                 total = 0, ret;
 214         int                     i;
 215 
 216         if (count == 0)
 217                 return (0);
 218 
 219         if (count < 0 || count > LX_IOV_MAX)
 220                 return (-EINVAL);
 221 
 222         iov = SAFE_ALLOCA(count * sizeof (struct iovec));
 223         if (iov == NULL)
 224                 return (-ENOMEM);
 225         if ((ret = lx_iovec_copy_and_check(iovp, iov, count)) != 0)
 226                 return (ret);
 227 
 228         for (i = 0; i < count; i++) {
 229                 ret = write(fd, iov[i].iov_base, iov[i].iov_len);
 230 
 231                 if (ret < 0) {
 232                         if (total > 0)
 233                                 return (total);
 234                         return (-errno);
 235                 }
 236 
 237                 total += ret;
 238         }
 239 
 240         return (total);
 241 }