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 }