Make the seek a method of the struct fileops.

Tested by:	pho
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Konstantin Belousov 2013-08-21 17:36:01 +00:00
parent 41cf41fdfd
commit c0a46535c4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=254602
4 changed files with 86 additions and 65 deletions

View File

@ -1697,6 +1697,7 @@ static struct fileops devfs_ops_f = {
.fo_chmod = vn_chmod,
.fo_chown = vn_chown,
.fo_sendfile = vn_sendfile,
.fo_seek = vn_seek,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};

View File

@ -1879,77 +1879,15 @@ sys_lseek(td, uap)
int whence;
} */ *uap;
{
struct ucred *cred = td->td_ucred;
struct file *fp;
struct vnode *vp;
struct vattr vattr;
off_t foffset, offset, size;
int error, noneg;
int error;
AUDIT_ARG_FD(uap->fd);
if ((error = fget(td, uap->fd, CAP_SEEK, &fp)) != 0)
return (error);
if (!(fp->f_ops->fo_flags & DFLAG_SEEKABLE)) {
fdrop(fp, td);
return (ESPIPE);
}
vp = fp->f_vnode;
foffset = foffset_lock(fp, 0);
noneg = (vp->v_type != VCHR);
offset = uap->offset;
switch (uap->whence) {
case L_INCR:
if (noneg &&
(foffset < 0 ||
(offset > 0 && foffset > OFF_MAX - offset))) {
error = EOVERFLOW;
break;
}
offset += foffset;
break;
case L_XTND:
vn_lock(vp, LK_SHARED | LK_RETRY);
error = VOP_GETATTR(vp, &vattr, cred);
VOP_UNLOCK(vp, 0);
if (error)
break;
/*
* If the file references a disk device, then fetch
* the media size and use that to determine the ending
* offset.
*/
if (vattr.va_size == 0 && vp->v_type == VCHR &&
fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0)
vattr.va_size = size;
if (noneg &&
(vattr.va_size > OFF_MAX ||
(offset > 0 && vattr.va_size > OFF_MAX - offset))) {
error = EOVERFLOW;
break;
}
offset += vattr.va_size;
break;
case L_SET:
break;
case SEEK_DATA:
error = fo_ioctl(fp, FIOSEEKDATA, &offset, cred, td);
break;
case SEEK_HOLE:
error = fo_ioctl(fp, FIOSEEKHOLE, &offset, cred, td);
break;
default:
error = EINVAL;
}
if (error == 0 && noneg && offset < 0)
error = EINVAL;
if (error != 0)
goto drop;
VFS_KNOTE_UNLOCKED(vp, 0);
*(off_t *)(td->td_retval) = offset;
drop:
error = (fp->f_ops->fo_flags & DFLAG_SEEKABLE) != 0 ?
fo_seek(fp, uap->offset, uap->whence, td) : ESPIPE;
fdrop(fp, td);
foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0);
return (error);
}

View File

@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/disk.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/kdb.h>
@ -101,6 +102,7 @@ struct fileops vnops = {
.fo_chmod = vn_chmod,
.fo_chown = vn_chown,
.fo_sendfile = vn_sendfile,
.fo_seek = vn_seek,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};
@ -2010,3 +2012,72 @@ unlock:
*off = noff;
return (error);
}
int
vn_seek(struct file *fp, off_t offset, int whence, struct thread *td)
{
struct ucred *cred;
struct vnode *vp;
struct vattr vattr;
off_t foffset, size;
int error, noneg;
cred = td->td_ucred;
vp = fp->f_vnode;
foffset = foffset_lock(fp, 0);
noneg = (vp->v_type != VCHR);
error = 0;
switch (whence) {
case L_INCR:
if (noneg &&
(foffset < 0 ||
(offset > 0 && foffset > OFF_MAX - offset))) {
error = EOVERFLOW;
break;
}
offset += foffset;
break;
case L_XTND:
vn_lock(vp, LK_SHARED | LK_RETRY);
error = VOP_GETATTR(vp, &vattr, cred);
VOP_UNLOCK(vp, 0);
if (error)
break;
/*
* If the file references a disk device, then fetch
* the media size and use that to determine the ending
* offset.
*/
if (vattr.va_size == 0 && vp->v_type == VCHR &&
fo_ioctl(fp, DIOCGMEDIASIZE, &size, cred, td) == 0)
vattr.va_size = size;
if (noneg &&
(vattr.va_size > OFF_MAX ||
(offset > 0 && vattr.va_size > OFF_MAX - offset))) {
error = EOVERFLOW;
break;
}
offset += vattr.va_size;
break;
case L_SET:
break;
case SEEK_DATA:
error = fo_ioctl(fp, FIOSEEKDATA, &offset, cred, td);
break;
case SEEK_HOLE:
error = fo_ioctl(fp, FIOSEEKHOLE, &offset, cred, td);
break;
default:
error = EINVAL;
}
if (error == 0 && noneg && offset < 0)
error = EINVAL;
if (error != 0)
goto drop;
VFS_KNOTE_UNLOCKED(vp, 0);
*(off_t *)(td->td_retval) = offset;
drop:
foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0);
return (error);
}

View File

@ -108,6 +108,8 @@ typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid,
typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes,
off_t *sent, int flags, int kflags, struct thread *td);
typedef int fo_seek_t(struct file *fp, off_t offset, int whence,
struct thread *td);
typedef int fo_flags_t;
struct fileops {
@ -122,6 +124,7 @@ struct fileops {
fo_chmod_t *fo_chmod;
fo_chown_t *fo_chown;
fo_sendfile_t *fo_sendfile;
fo_seek_t *fo_seek;
fo_flags_t fo_flags; /* DFLAG_* below */
};
@ -242,6 +245,7 @@ fo_chown_t invfo_chown;
fo_sendfile_t invfo_sendfile;
fo_sendfile_t vn_sendfile;
fo_seek_t vn_seek;
void finit(struct file *, u_int, short, void *, struct fileops *);
int fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp);
@ -370,6 +374,13 @@ fo_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio,
nbytes, sent, flags, kflags, td));
}
static __inline int
fo_seek(struct file *fp, off_t offset, int whence, struct thread *td)
{
return ((*fp->f_ops->fo_seek)(fp, offset, whence, td));
}
#endif /* _KERNEL */
#endif /* !SYS_FILE_H */