Home | History | Annotate | Download | only in common
      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 }
    242