#include <sys/param.h>
#include <sys/isa_defs.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/cred.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <sys/filio.h>
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_DATA 3
#define SEEK_HOLE 4
#if defined(_SYSCALL32_IMPL) || defined(_ILP32)
static int
lseek32_common(file_t *fp, int stype, offset_t off, offset_t max,
offset_t *retoff)
{
vnode_t *vp;
struct vattr vattr;
int error;
u_offset_t noff;
offset_t curoff, newoff;
int reg;
vp = fp->f_vnode;
reg = (vp->v_type == VREG);
curoff = fp->f_offset;
switch (stype) {
case SEEK_SET:
noff = (u_offset_t)off;
if (reg && noff > max) {
error = EINVAL;
goto out;
}
break;
case SEEK_CUR:
if (reg && off > (max - curoff)) {
error = EOVERFLOW;
goto out;
}
noff = (u_offset_t)(off + curoff);
if (reg && noff > max) {
error = EINVAL;
goto out;
}
break;
case SEEK_END:
vattr.va_mask = AT_SIZE;
if (error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL)) {
goto out;
}
if (reg && (off > (max - (offset_t)vattr.va_size))) {
error = EOVERFLOW;
goto out;
}
noff = (u_offset_t)(off + (offset_t)vattr.va_size);
if (reg && noff > max) {
error = EINVAL;
goto out;
}
break;
case SEEK_DATA:
noff = (u_offset_t)off;
error = VOP_IOCTL(vp, _FIO_SEEK_DATA, (intptr_t)(&noff),
FKIOCTL, kcred, NULL, NULL);
if (error) {
if (error != ENOTTY)
return (error);
vattr.va_mask = AT_SIZE;
error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
if (error)
return (error);
if (noff >= (u_offset_t)vattr.va_size)
return (ENXIO);
}
if (reg && (noff > max))
return (EOVERFLOW);
fp->f_offset = (offset_t)noff;
(*retoff) = (offset_t)noff;
return (0);
case SEEK_HOLE:
noff = (u_offset_t)off;
error = VOP_IOCTL(vp, _FIO_SEEK_HOLE, (intptr_t)(&noff),
FKIOCTL, kcred, NULL, NULL);
if (error) {
if (error != ENOTTY)
return (error);
vattr.va_mask = AT_SIZE;
error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
if (error)
return (error);
if (off < (offset_t)vattr.va_size)
noff = (u_offset_t)vattr.va_size;
else
return (ENXIO);
}
if (reg && (noff > max))
return (EOVERFLOW);
fp->f_offset = (offset_t)noff;
(*retoff) = (offset_t)noff;
return (0);
default:
error = EINVAL;
goto out;
}
ASSERT((reg && noff <= max) || !reg);
newoff = (offset_t)noff;
if ((error = VOP_SEEK(vp, curoff, &newoff, NULL)) == 0) {
fp->f_offset = newoff;
(*retoff) = newoff;
return (0);
}
out:
return (error);
}
off32_t
lseek32(int32_t fdes, off32_t off, int32_t stype)
{
file_t *fp;
int error;
offset_t retoff;
if ((fp = getf(fdes)) == NULL)
return ((off32_t)set_errno(EBADF));
if (fp->f_vnode->v_type == VREG || stype != SEEK_SET)
error = lseek32_common(fp, stype, (offset_t)off,
(offset_t)MAXOFF32_T, &retoff);
else if (stype == SEEK_SET)
error = lseek32_common(fp, stype, (offset_t)(uint_t)off,
(offset_t)(uint_t)UINT_MAX, &retoff);
releasef(fdes);
if (!error)
return ((off32_t)retoff);
return ((off32_t)set_errno(error));
}
offset_t
llseek32(int32_t fdes, uint32_t off1, uint32_t off2, int stype)
{
file_t *fp;
int error;
offset_t retoff;
#if defined(_LITTLE_ENDIAN)
offset_t off = ((u_offset_t)off2 << 32) | (u_offset_t)off1;
#else
offset_t off = ((u_offset_t)off1 << 32) | (u_offset_t)off2;
#endif
if ((fp = getf(fdes)) == NULL)
error = EBADF;
else {
error = lseek32_common(fp, stype, off, MAXOFFSET_T, &retoff);
releasef(fdes);
}
return (error ? (offset_t)set_errno(error) : retoff);
}
#endif
#ifdef _LP64
off_t
lseek64(int fdes, off_t off, int stype)
{
file_t *fp;
vnode_t *vp;
struct vattr vattr;
int error;
off_t old_off;
offset_t new_off;
if ((fp = getf(fdes)) == NULL)
return ((off_t)set_errno(EBADF));
vp = fp->f_vnode;
new_off = off;
switch (stype) {
case SEEK_CUR:
new_off += fp->f_offset;
break;
case SEEK_END:
vattr.va_mask = AT_SIZE;
if ((error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL)) != 0)
goto lseek64error;
new_off += vattr.va_size;
break;
case SEEK_SET:
break;
case SEEK_DATA:
new_off = (offset_t)off;
error = VOP_IOCTL(vp, _FIO_SEEK_DATA, (intptr_t)(&new_off),
FKIOCTL, kcred, NULL, NULL);
if (error) {
if (error != ENOTTY) {
goto lseek64error;
}
vattr.va_mask = AT_SIZE;
error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
if (error)
goto lseek64error;
if (new_off >= (offset_t)vattr.va_size) {
error = ENXIO;
goto lseek64error;
}
}
fp->f_offset = new_off;
releasef(fdes);
return (new_off);
case SEEK_HOLE:
new_off = off;
error = VOP_IOCTL(vp, _FIO_SEEK_HOLE, (intptr_t)(&new_off),
FKIOCTL, kcred, NULL, NULL);
if (error) {
if (error != ENOTTY)
goto lseek64error;
vattr.va_mask = AT_SIZE;
error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
if (error)
goto lseek64error;
if (off < (offset_t)vattr.va_size) {
new_off = (offset_t)vattr.va_size;
} else {
error = ENXIO;
goto lseek64error;
}
}
fp->f_offset = new_off;
releasef(fdes);
return (new_off);
default:
error = EINVAL;
goto lseek64error;
}
old_off = fp->f_offset;
if ((error = VOP_SEEK(vp, old_off, &new_off, NULL)) == 0) {
fp->f_offset = new_off;
releasef(fdes);
return (new_off);
}
lseek64error:
releasef(fdes);
return ((off_t)set_errno(error));
}
#endif