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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 4.3 BSD 31 * under license from the Regents of the University of California. 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 #include <sys/param.h> 37 #include <sys/isa_defs.h> 38 #include <sys/types.h> 39 #include <sys/sysmacros.h> 40 #include <sys/cred.h> 41 #include <sys/systm.h> 42 #include <sys/errno.h> 43 #include <sys/vnode.h> 44 #include <sys/file.h> 45 #include <sys/debug.h> 46 #include <sys/cmn_err.h> 47 #include <sys/filio.h> 48 49 /* 50 * These are defined in unistd.h - but we can't include that 51 */ 52 #define SEEK_SET 0 /* Set file pointer to "offset" */ 53 #define SEEK_CUR 1 /* Set file pointer to current plus "offset" */ 54 #define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ 55 #define SEEK_DATA 3 /* Set file pointer to next data past offset */ 56 #define SEEK_HOLE 4 /* Set file pointer to next hole past offset */ 57 58 /* 59 * Seek on a file 60 */ 61 62 #if defined(_SYSCALL32_IMPL) || defined(_ILP32) 63 /* 64 * Workhorse for the 32-bit seek variants: lseek32 and llseek32 65 * 66 * 'max' represents the maximum possible representation of offset 67 * in the data type corresponding to lseek and llseek. It is 68 * MAXOFF32_T for off32_t and MAXOFFSET_T for off64_t. 69 * We return EOVERFLOW if we cannot represent the resulting offset 70 * in the data type. 71 * We provide support for character devices to be seeked beyond MAXOFF32_T 72 * by lseek. To maintain compatibility in such cases lseek passes 73 * the arguments carefully to lseek_common when file is not regular. 74 * (/dev/kmem is a good example of a > 2Gbyte seek!) 75 */ 76 static int 77 lseek32_common(file_t *fp, int stype, offset_t off, offset_t max, 78 offset_t *retoff) 79 { 80 vnode_t *vp; 81 struct vattr vattr; 82 int error; 83 u_offset_t noff; 84 offset_t curoff, newoff; 85 int reg; 86 87 vp = fp->f_vnode; 88 reg = (vp->v_type == VREG); 89 90 curoff = fp->f_offset; 91 92 switch (stype) { 93 case SEEK_SET: 94 noff = (u_offset_t)off; 95 if (reg && noff > max) { 96 error = EINVAL; 97 goto out; 98 } 99 break; 100 101 case SEEK_CUR: 102 if (reg && off > (max - curoff)) { 103 error = EOVERFLOW; 104 goto out; 105 } 106 noff = (u_offset_t)(off + curoff); 107 if (reg && noff > max) { 108 error = EINVAL; 109 goto out; 110 } 111 break; 112 113 case SEEK_END: 114 vattr.va_mask = AT_SIZE; 115 if (error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL)) { 116 goto out; 117 } 118 if (reg && (off > (max - (offset_t)vattr.va_size))) { 119 error = EOVERFLOW; 120 goto out; 121 } 122 noff = (u_offset_t)(off + (offset_t)vattr.va_size); 123 if (reg && noff > max) { 124 error = EINVAL; 125 goto out; 126 } 127 break; 128 129 case SEEK_DATA: 130 /* 131 * Get and set the file pointer to the offset of the next 132 * data past "off" 133 */ 134 noff = (u_offset_t)off; 135 error = VOP_IOCTL(vp, _FIO_SEEK_DATA, (intptr_t)(&noff), 136 FKIOCTL, kcred, NULL, NULL); 137 if (error) { 138 if (error != ENOTTY) 139 return (error); 140 /* 141 * The ioctl is not supported, check the supplied 142 * "off" is not past the end of file 143 */ 144 vattr.va_mask = AT_SIZE; 145 error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); 146 if (error) 147 return (error); 148 if (noff >= (u_offset_t)vattr.va_size) 149 return (ENXIO); 150 } 151 if (reg && (noff > max)) 152 return (EOVERFLOW); 153 154 fp->f_offset = (offset_t)noff; 155 (*retoff) = (offset_t)noff; 156 return (0); 157 158 case SEEK_HOLE: 159 /* 160 * Get and set the file pointer to the offset of the next 161 * hole past "off" 162 */ 163 noff = (u_offset_t)off; 164 error = VOP_IOCTL(vp, _FIO_SEEK_HOLE, (intptr_t)(&noff), 165 FKIOCTL, kcred, NULL, NULL); 166 if (error) { 167 if (error != ENOTTY) 168 return (error); 169 /* 170 * ioctl is not supported, if the off is valid return 171 * the "virtual hole" at the end of the file. 172 */ 173 vattr.va_mask = AT_SIZE; 174 error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); 175 if (error) 176 return (error); 177 if (off < (offset_t)vattr.va_size) 178 noff = (u_offset_t)vattr.va_size; 179 else 180 return (ENXIO); 181 } 182 if (reg && (noff > max)) 183 return (EOVERFLOW); 184 185 fp->f_offset = (offset_t)noff; 186 (*retoff) = (offset_t)noff; 187 return (0); 188 189 default: 190 error = EINVAL; 191 goto out; 192 } 193 194 ASSERT((reg && noff <= max) || !reg); 195 newoff = (offset_t)noff; 196 if ((error = VOP_SEEK(vp, curoff, &newoff, NULL)) == 0) { 197 fp->f_offset = newoff; 198 (*retoff) = newoff; 199 return (0); 200 } 201 out: 202 return (error); 203 } 204 205 off32_t 206 lseek32(int32_t fdes, off32_t off, int32_t stype) 207 { 208 file_t *fp; 209 int error; 210 offset_t retoff; 211 212 if ((fp = getf(fdes)) == NULL) 213 return ((off32_t)set_errno(EBADF)); 214 215 /* 216 * lseek32 returns EOVERFLOW if we cannot represent the resulting 217 * offset from seek in a 32-bit off_t. 218 * The following routines are sensitive to sign extensions and 219 * calculations and if ever you change this make sure it works for 220 * special files. 221 * 222 * When VREG is not set we do the check for stype != SEEK_SET 223 * to send the unsigned value to lseek_common and not the sign 224 * extended value. (The maximum representable value is not 225 * checked by lseek_common for special files.) 226 */ 227 if (fp->f_vnode->v_type == VREG || stype != SEEK_SET) 228 error = lseek32_common(fp, stype, (offset_t)off, 229 (offset_t)MAXOFF32_T, &retoff); 230 else if (stype == SEEK_SET) 231 error = lseek32_common(fp, stype, (offset_t)(uint_t)off, 232 (offset_t)(uint_t)UINT_MAX, &retoff); 233 234 releasef(fdes); 235 if (!error) 236 return ((off32_t)retoff); 237 return ((off32_t)set_errno(error)); 238 } 239 240 /* 241 * 64-bit seeks from 32-bit applications 242 */ 243 offset_t 244 llseek32(int32_t fdes, uint32_t off1, uint32_t off2, int stype) 245 { 246 file_t *fp; 247 int error; 248 offset_t retoff; 249 #if defined(_LITTLE_ENDIAN) 250 offset_t off = ((u_offset_t)off2 << 32) | (u_offset_t)off1; 251 #else 252 offset_t off = ((u_offset_t)off1 << 32) | (u_offset_t)off2; 253 #endif 254 255 if ((fp = getf(fdes)) == NULL) 256 error = EBADF; 257 else { 258 error = lseek32_common(fp, stype, off, MAXOFFSET_T, &retoff); 259 releasef(fdes); 260 } 261 262 return (error ? (offset_t)set_errno(error) : retoff); 263 } 264 #endif /* _SYSCALL32_IMPL || _ILP32 */ 265 266 #ifdef _LP64 267 /* 268 * Seek on a file. 269 * 270 * Life is almost simple again (at least until we do 128-bit files ;-) 271 * This is both 'lseek' and 'llseek' to a 64-bit application. 272 */ 273 off_t 274 lseek64(int fdes, off_t off, int stype) 275 { 276 file_t *fp; 277 vnode_t *vp; 278 struct vattr vattr; 279 int error; 280 off_t old_off; 281 offset_t new_off; 282 283 if ((fp = getf(fdes)) == NULL) 284 return ((off_t)set_errno(EBADF)); 285 286 vp = fp->f_vnode; 287 new_off = off; 288 289 switch (stype) { 290 case SEEK_CUR: 291 new_off += fp->f_offset; 292 break; 293 294 case SEEK_END: 295 vattr.va_mask = AT_SIZE; 296 if ((error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL)) != 0) 297 goto lseek64error; 298 new_off += vattr.va_size; 299 break; 300 301 case SEEK_SET: 302 break; 303 304 case SEEK_DATA: 305 /* 306 * Get and set the file pointer to the offset of the next 307 * data past "off" 308 */ 309 new_off = (offset_t)off; 310 error = VOP_IOCTL(vp, _FIO_SEEK_DATA, (intptr_t)(&new_off), 311 FKIOCTL, kcred, NULL, NULL); 312 if (error) { 313 if (error != ENOTTY) { 314 goto lseek64error; 315 } 316 /* 317 * The ioctl is not supported, check the supplied off 318 * is not past end of file 319 */ 320 vattr.va_mask = AT_SIZE; 321 error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); 322 if (error) 323 goto lseek64error; 324 if (new_off >= (offset_t)vattr.va_size) { 325 error = ENXIO; 326 goto lseek64error; 327 } 328 } 329 fp->f_offset = new_off; 330 releasef(fdes); 331 return (new_off); 332 333 case SEEK_HOLE: 334 /* 335 * Get and set the file pointer to the offset of the next 336 * hole past "off" 337 */ 338 new_off = off; 339 error = VOP_IOCTL(vp, _FIO_SEEK_HOLE, (intptr_t)(&new_off), 340 FKIOCTL, kcred, NULL, NULL); 341 if (error) { 342 if (error != ENOTTY) 343 goto lseek64error; 344 /* 345 * ioctl is not supported, if the off is valid return 346 * the "virtual hole" at the end of the file. 347 */ 348 vattr.va_mask = AT_SIZE; 349 error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL); 350 if (error) 351 goto lseek64error; 352 if (off < (offset_t)vattr.va_size) { 353 new_off = (offset_t)vattr.va_size; 354 } else { 355 error = ENXIO; 356 goto lseek64error; 357 } 358 } 359 fp->f_offset = new_off; 360 releasef(fdes); 361 return (new_off); 362 363 default: 364 error = EINVAL; 365 goto lseek64error; 366 } 367 368 old_off = fp->f_offset; 369 if ((error = VOP_SEEK(vp, old_off, &new_off, NULL)) == 0) { 370 fp->f_offset = new_off; 371 releasef(fdes); 372 return (new_off); 373 } 374 375 lseek64error: 376 releasef(fdes); 377 return ((off_t)set_errno(error)); 378 } 379 #endif /* _LP64 */ 380