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