Home | History | Annotate | Download | only in syscall
      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	"@(#)lseek.c	1.19	07/10/25 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