/*
* Copyright (c) 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95
*/
/*
* Copyright (c) 1993 Jan-Simon Pendry
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95
*/
int ptyfs_lookup (void *);
int ptyfs_open (void *);
int ptyfs_close (void *);
int ptyfs_access (void *);
int ptyfs_getattr (void *);
int ptyfs_setattr (void *);
int ptyfs_read (void *);
int ptyfs_write (void *);
int ptyfs_ioctl (void *);
int ptyfs_poll (void *);
int ptyfs_kqfilter (void *);
int ptyfs_readdir (void *);
int ptyfs_reclaim (void *);
int ptyfs_inactive (void *);
int ptyfs_print (void *);
int ptyfs_pathconf (void *);
int ptyfs_advlock (void *);
/*
* support advisory locking on pty nodes
*/
int
ptyfs_advlock(void *v)
{
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap = v;
struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp);
switch (ptyfs->ptyfs_type) {
case PTYFSpts:
case PTYFSptc:
return spec_advlock(v);
default:
return EOPNOTSUPP;
}
}
/*
* Invent attributes for ptyfsnode (vp) and store
* them in (vap).
* Directories lengths are returned as zero since
* any real length would require the genuine size
* to be computed, and nothing cares anyway.
*
* this is relatively minimal for ptyfs.
*/
int
ptyfs_getattr(void *v)
{
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
kauth_cred_t a_cred;
} */ *ap = v;
struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp);
struct vattr *vap = ap->a_vap;
PTYFS_ITIMES(ptyfs, NULL, NULL, NULL);
/* start by zeroing out the attributes */
vattr_null(vap);
/* next do all the common fields */
vap->va_type = ap->a_vp->v_type;
vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
vap->va_fileid = ptyfs->ptyfs_fileno;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_blocksize = PAGE_SIZE;
/*
* Go through the fields and update iff not VNOVAL.
*/
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return EROFS;
error = ptyfs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
if (error)
return error;
}
if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
vap->va_birthtime.tv_sec != VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return EROFS;
if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0)
return EPERM;
error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
NULL, genfs_can_chtimes(vp, cred, ptyfs->ptyfs_uid,
vap->va_vaflags));
if (error)
return (error);
if (vap->va_atime.tv_sec != VNOVAL)
if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
ptyfs->ptyfs_status |= PTYFS_ACCESS;
if (vap->va_mtime.tv_sec != VNOVAL) {
ptyfs->ptyfs_status |= PTYFS_CHANGE | PTYFS_MODIFY;
if (vp->v_mount->mnt_flag & MNT_RELATIME)
ptyfs->ptyfs_status |= PTYFS_ACCESS;
}
if (vap->va_birthtime.tv_sec != VNOVAL)
ptyfs->ptyfs_birthtime = vap->va_birthtime;
ptyfs->ptyfs_status |= PTYFS_CHANGE;
error = ptyfs_update(vp, &vap->va_atime, &vap->va_mtime, 0);
if (error)
return error;
}
if (vap->va_mode != (mode_t)VNOVAL) {
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return EROFS;
if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0 &&
(vap->va_mode &
(S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IXOTH|S_IWOTH)))
return EPERM;
error = ptyfs_chmod(vp, vap->va_mode, cred, l);
if (error)
return error;
}
return 0;
}
/*
* Change the mode on a file.
* Inode must be locked before calling.
*/
static int
ptyfs_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred, struct lwp *l)
{
struct ptyfsnode *ptyfs = VTOPTYFS(vp);
int error;
/*
* implement access checking.
*
* actually, the check for super-user is slightly
* broken since it will allow read access to write-only
* objects. this doesn't cause any particular trouble
* but does mean that the i/o entry points need to check
* that the operation really does make sense.
*/
int
ptyfs_access(void *v)
{
struct vop_access_args /* {
struct vnode *a_vp;
accmode_t a_accmode;
kauth_cred_t a_cred;
} */ *ap = v;
struct vattr va;
int error;
if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred)) != 0)
return error;
/*
* lookup. this is incredibly complicated in the
* general case, however for most pseudo-filesystems
* very little needs to be done.
*
* Locking isn't hard here, just poorly documented.
*
* If we're looking up ".", just vref the parent & return it.
*
* If we're looking up "..", unlock the parent, and lock "..". If everything
* went ok, try to re-lock the parent. We do this to prevent lock races.
*
* For anything else, get the needed node.
*
* We try to exit with the parent locked in error cases.
*/
int
ptyfs_lookup(void *v)
{
struct vop_lookup_v2_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap = v;
struct componentname *cnp = ap->a_cnp;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
const char *pname = cnp->cn_nameptr;
struct ptyfsnode *ptyfs;
int pty, error;
*vpp = NULL;
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
return EROFS;
ptyfs = VTOPTYFS(dvp);
switch (ptyfs->ptyfs_type) {
case PTYFSroot:
/*
* Shouldn't get here with .. in the root node.
*/
if (cnp->cn_flags & ISDOTDOT)
return EIO;
/*
* readdir returns directory entries from ptyfsnode (vp).
*
* the strategy here with ptyfs is to generate a single
* directory entry at a time (struct dirent) and then
* copy that out to userland using uiomove. a more efficient
* though more complex implementation, would try to minimize
* the number of calls to uiomove(). for ptyfs, this is
* hardly worth the added code complexity.
*
* this should just be done through read()
*/
int
ptyfs_readdir(void *v)
{
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
kauth_cred_t a_cred;
int *a_eofflag;
off_t **a_cookies;
int *a_ncookies;
} */ *ap = v;
struct uio *uio = ap->a_uio;
struct dirent *dp;
struct ptyfsnode *ptyfs;
off_t i;
int error;
off_t *cookies = NULL;
int ncookies;
struct vnode *vp;
int n, nc = 0;
vp = ap->a_vp;
ptyfs = VTOPTYFS(vp);
if (uio->uio_resid < UIO_MX)
return EINVAL;
if (uio->uio_offset < 0)
return EINVAL;