diff --git a/sys/miscfs/Makefile b/sys/miscfs/Makefile index d23b95bfaab4..97fa1dc856c8 100644 --- a/sys/miscfs/Makefile +++ b/sys/miscfs/Makefile @@ -1,7 +1,7 @@ -# $NetBSD: Makefile,v 1.5 2003/03/16 08:26:47 jdolecek Exp $ +# $NetBSD: Makefile,v 1.6 2004/11/11 01:40:32 christos Exp $ SUBDIR= fdesc fifofs genfs kernfs nullfs overlay portal -SUBDIR+= procfs specfs syncfs umapfs +SUBDIR+= procfs ptyfs specfs syncfs umapfs INCSDIR= /usr/include/miscfs diff --git a/sys/miscfs/ptyfs/Makefile b/sys/miscfs/ptyfs/Makefile new file mode 100644 index 000000000000..fa3b7ca86062 --- /dev/null +++ b/sys/miscfs/ptyfs/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2004/11/11 01:40:32 christos Exp $ + +INCSDIR= /usr/include/miscfs/ptyfs + +INCS= ptyfs.h + +.include diff --git a/sys/miscfs/ptyfs/files.ptyfs b/sys/miscfs/ptyfs/files.ptyfs new file mode 100644 index 000000000000..51ef6a958f02 --- /dev/null +++ b/sys/miscfs/ptyfs/files.ptyfs @@ -0,0 +1,7 @@ +# $NetBSD: files.ptyfs,v 1.1 2004/11/11 01:40:32 christos Exp $ + +deffs fs_ptyfs.h PTYFS # XXX + +file miscfs/ptyfs/ptyfs_subr.c ptyfs +file miscfs/ptyfs/ptyfs_vfsops.c ptyfs +file miscfs/ptyfs/ptyfs_vnops.c ptyfs diff --git a/sys/miscfs/ptyfs/ptyfs.h b/sys/miscfs/ptyfs/ptyfs.h new file mode 100644 index 000000000000..d239db7d4e6f --- /dev/null +++ b/sys/miscfs/ptyfs/ptyfs.h @@ -0,0 +1,151 @@ +/* $NetBSD: ptyfs.h,v 1.1 2004/11/11 01:40:32 christos Exp $ */ + +/* + * Copyright (c) 1993 + * 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. + * + * @(#)ptyfs.h 8.9 (Berkeley) 5/14/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. + * + * @(#)ptyfs.h 8.9 (Berkeley) 5/14/95 + */ + +#ifdef _KERNEL +/* + * The different types of node in a ptyfs filesystem + */ +typedef enum { + PTYFSpts, /* The slave side of a pty */ + PTYFSptc, /* The controlling side of a pty */ + PTYFSroot, /* the filesystem root */ +} ptyfstype; + +/* + * control data for the proc file system. + */ +struct ptyfsnode { + LIST_ENTRY(ptyfsnode) ptyfs_hash; /* hash chain */ + struct vnode *ptyfs_vnode; /* vnode associated with this ptyfsnode */ + ptyfstype ptyfs_type; /* type of ptyfs node */ + int ptyfs_pty; /* the pty index */ + u_long ptyfs_fileno; /* unique file id */ + int ptyfs_flag; /* status flag for times */ +#define PTYFS_ACCESS 1 +#define PTYFS_MODIFY 2 +#define PTYFS_CHANGE 4 + /* Attribute information */ + uid_t ptyfs_uid; + gid_t ptyfs_gid; + mode_t ptyfs_mode; + int ptyfs_flags; + struct timespec ptyfs_ctime, ptyfs_mtime, ptyfs_atime, ptyfs_birthtime; +}; + +#endif /* _KERNEL */ + +/* + * Kernel stuff follows + */ +#ifdef _KERNEL +#define CNEQ(cnp, s, len) \ + ((cnp)->cn_namelen == (len) && \ + (memcmp((s), (cnp)->cn_nameptr, (len)) == 0)) + +#define UIO_MX 32 + +#define PTYFS_FILENO(pty, type) \ + ((((uint32_t)type) << 30) | (pty + 1)) + +#define PTYFS_MAKEDEV(ptyfs) \ + pty_makedev((ptyfs)->ptyfs_type == PTYFSpts ? 't' : 'p', (ptyfs)->ptyfs_pty) + +/* + * Convert between ptyfsnode vnode + */ +#define VTOPTYFS(vp) ((struct ptyfsnode *)(vp)->v_data) +#define PTYFSTOV(ptyfs) ((ptyfs)->ptyfs_vnode) + +int ptyfs_freevp(struct vnode *); +int ptyfs_allocvp(struct mount *, struct vnode **, ptyfstype, int, + struct proc *); +void ptyfs_hashinit(void); +void ptyfs_hashreinit(void); +void ptyfs_hashdone(void); +int ptyfs_getfp(struct ptyfsnode *, struct proc **, struct file **); + +/* functions to check whether or not files should be displayed */ +int ptyfs_validfile(struct proc *, struct mount *); +int ptyfs_validfpregs(struct proc *, struct mount *); +int ptyfs_validregs(struct proc *, struct mount *); +int ptyfs_validmap(struct proc *, struct mount *); + +extern int (**ptyfs_vnodeop_p)(void *); +extern struct vfsops ptyfs_vfsops; + +int ptyfs_root(struct mount *, struct vnode **); + +#ifdef SYSCTL_SETUP_PROTO +SYSCTL_SETUP_PROTO(sysctl_vfs_ptyfs_setup); +#endif /* SYSCTL_SETUP_PROTO */ +#endif /* _KERNEL */ diff --git a/sys/miscfs/ptyfs/ptyfs_subr.c b/sys/miscfs/ptyfs/ptyfs_subr.c new file mode 100644 index 000000000000..4ed50f180fc4 --- /dev/null +++ b/sys/miscfs/ptyfs/ptyfs_subr.c @@ -0,0 +1,421 @@ +/* $NetBSD: ptyfs_subr.c,v 1.1 2004/11/11 01:40:32 christos Exp $ */ + +/* + * Copyright (c) 1993 + * 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. + * + * @(#)ptyfs_subr.c 8.6 (Berkeley) 5/14/95 + */ + +/* + * Copyright (c) 1994 Christopher G. Demetriou. All rights reserved. + * 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_subr.c 8.6 (Berkeley) 5/14/95 + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: ptyfs_subr.c,v 1.1 2004/11/11 01:40:32 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct lock ptyfs_hashlock; + +static LIST_HEAD(ptyfs_hashhead, ptyfsnode) *ptyfs_used_tbl, *ptyfs_free_tbl; +static u_long ptyfs_used_mask, ptyfs_free_mask; /* size of hash table - 1 */ +static struct simplelock ptyfs_used_slock, ptyfs_free_slock; + +static void ptyfs_getinfo(struct ptyfsnode *, struct proc *); + +static void ptyfs_hashins(struct ptyfsnode *); +static void ptyfs_hashrem(struct ptyfsnode *); + +static struct vnode *ptyfs_used_get(ptyfstype, int, struct mount *); +static struct ptyfsnode *ptyfs_free_get(ptyfstype, int, struct proc *); + +static void ptyfs_rehash(struct simplelock *, struct ptyfs_hashhead **, + u_long *); + +#define PTYHASH(type, pty, mask) (PTYFS_FILENO(type, pty) % (mask + 1)) + + +static void +ptyfs_getinfo(struct ptyfsnode *ptyfs, struct proc *p) +{ + extern struct ptm_pty *ptyfs_save_ptm, ptm_ptyfspty; + + if (ptyfs->ptyfs_type == PTYFSroot) { + ptyfs->ptyfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP| + S_IROTH|S_IXOTH; + goto out; + } else + ptyfs->ptyfs_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP| + S_IROTH|S_IWOTH; + + if (ptyfs_save_ptm != NULL && ptyfs_save_ptm != &ptm_ptyfspty) { + int error; + struct nameidata nd; + char ttyname[64]; + struct ucred *cred; + struct vattr va; + /* + * We support traditional ptys, so we copy the info + * from the inode + */ + if ((error = (*ptyfs_save_ptm->makename)( + ptyfs_save_ptm, ttyname, sizeof(ttyname), + ptyfs->ptyfs_pty, ptyfs->ptyfs_type == PTYFSpts ? 't' + : 'p')) != 0) + goto out; + NDINIT(&nd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE, ttyname, + p); + if ((error = namei(&nd)) != 0) + goto out; + cred = crget(); + error = VOP_GETATTR(nd.ni_vp, &va, cred, p); + crfree(cred); + VOP_UNLOCK(nd.ni_vp, 0); + vrele(nd.ni_vp); + if (error) + goto out; + ptyfs->ptyfs_uid = va.va_uid; + ptyfs->ptyfs_gid = va.va_gid; + ptyfs->ptyfs_mode = va.va_mode; + ptyfs->ptyfs_flags = va.va_flags; + ptyfs->ptyfs_birthtime = va.va_birthtime; + ptyfs->ptyfs_ctime = va.va_ctime; + ptyfs->ptyfs_mtime = va.va_mtime; + ptyfs->ptyfs_atime = va.va_atime; + return; + } +out: + ptyfs->ptyfs_uid = ptyfs->ptyfs_gid = 0; + TIMEVAL_TO_TIMESPEC(&time, &ptyfs->ptyfs_ctime); + ptyfs->ptyfs_birthtime = ptyfs->ptyfs_mtime = + ptyfs->ptyfs_atime = ptyfs->ptyfs_ctime; + ptyfs->ptyfs_flags = 0; +} + + +/* + * allocate a ptyfsnode/vnode pair. the vnode is + * referenced, and locked. + * + * the pid, ptyfs_type, and mount point uniquely + * identify a ptyfsnode. the mount point is needed + * because someone might mount this filesystem + * twice. + * + * all ptyfsnodes are maintained on a singly-linked + * list. new nodes are only allocated when they cannot + * be found on this list. entries on the list are + * removed when the vfs reclaim entry is called. + * + * a single lock is kept for the entire list. this is + * needed because the getnewvnode() function can block + * waiting for a vnode to become free, in which case there + * may be more than one ptyess trying to get the same + * vnode. this lock is only taken if we are going to + * call getnewvnode, since the kernel itself is single-threaded. + * + * if an entry is found on the list, then call vget() to + * take a reference. this is done because there may be + * zero references to it and so it needs to removed from + * the vnode free list. + */ +int +ptyfs_allocvp(struct mount *mp, struct vnode **vpp, ptyfstype type, int pty, + struct proc *p) +{ + struct ptyfsnode *ptyfs; + struct vnode *vp, *nvp; + int error; + + do { + if ((*vpp = ptyfs_used_get(type, pty, mp)) != NULL) + return 0; + } while (lockmgr(&ptyfs_hashlock, LK_EXCLUSIVE|LK_SLEEPFAIL, 0)); + + if ((error = getnewvnode(VT_PTYFS, mp, ptyfs_vnodeop_p, &vp)) != 0) { + *vpp = NULL; + lockmgr(&ptyfs_hashlock, LK_RELEASE, NULL); + return error; + } + + vp->v_data = ptyfs = ptyfs_free_get(type, pty, p); + ptyfs->ptyfs_vnode = vp; + + switch (type) { + case PTYFSroot: /* /pts = dr-xr-xr-x */ + vp->v_type = VDIR; + vp->v_flag = VROOT; + break; + + case PTYFSpts: /* /pts/N = cxxxxxxxxx */ + case PTYFSptc: /* controlling side = cxxxxxxxxx */ + vp->v_type = VCHR; + if ((nvp = checkalias(vp, PTYFS_MAKEDEV(ptyfs), mp)) != NULL) { + /* + * Discard unneeded vnode, but save its inode. + */ + nvp->v_data = vp->v_data; + vp->v_data = NULL; + /* XXX spec_vnodeops has no locking, do it explicitly */ + VOP_UNLOCK(vp, 0); + vp->v_op = spec_vnodeop_p; + vp->v_flag &= ~VLOCKSWORK; + vrele(vp); + vgone(vp); + lockmgr(&nvp->v_lock, LK_EXCLUSIVE, &nvp->v_interlock); + /* + * Reinitialize aliased inode. + */ + vp = nvp; + ptyfs->ptyfs_vnode = vp; + } + break; + default: + panic("ptyfs_allocvp"); + } + + ptyfs_hashins(ptyfs); + uvm_vnp_setsize(vp, 0); + lockmgr(&ptyfs_hashlock, LK_RELEASE, NULL); + + *vpp = vp; + return 0; +} + +int +ptyfs_freevp(struct vnode *vp) +{ + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + + ptyfs_hashrem(ptyfs); + vp->v_data = NULL; + return 0; +} + +/* + * Initialize ptyfsnode hash table. + */ +void +ptyfs_hashinit(void) +{ + lockinit(&ptyfs_hashlock, PINOD, "ptyfs_hashlock", 0, 0); + ptyfs_used_tbl = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, + M_WAITOK, &ptyfs_used_mask); + ptyfs_free_tbl = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, + M_WAITOK, &ptyfs_free_mask); + simple_lock_init(&ptyfs_used_slock); + simple_lock_init(&ptyfs_free_slock); +} + +void +ptyfs_hashreinit(void) +{ + ptyfs_rehash(&ptyfs_used_slock, &ptyfs_used_tbl, &ptyfs_used_mask); + ptyfs_rehash(&ptyfs_free_slock, &ptyfs_free_tbl, &ptyfs_free_mask); +} + +static void +ptyfs_rehash(struct simplelock *hlock, struct ptyfs_hashhead **hhead, + u_long *hmask) +{ + struct ptyfsnode *pp; + struct ptyfs_hashhead *oldhash, *hash; + u_long i, oldmask, mask, val; + + hash = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, M_WAITOK, + &mask); + + simple_lock(hlock); + oldhash = *hhead; + oldmask = *hmask; + *hhead = hash; + *hmask = mask; + for (i = 0; i <= oldmask; i++) { + while ((pp = LIST_FIRST(&oldhash[i])) != NULL) { + LIST_REMOVE(pp, ptyfs_hash); + val = PTYHASH(pp->ptyfs_type, pp->ptyfs_pty, + ptyfs_used_mask); + LIST_INSERT_HEAD(&hash[val], pp, ptyfs_hash); + } + } + simple_unlock(hlock); + hashdone(oldhash, M_UFSMNT); +} + +/* + * Free ptyfsnode hash table. + */ +void +ptyfs_hashdone(void) +{ + hashdone(ptyfs_used_tbl, M_UFSMNT); + hashdone(ptyfs_free_tbl, M_UFSMNT); +} + +/* + * Get a ptyfsnode from the free table, or allocate one. + * Removes the node from the free table. + */ +struct ptyfsnode * +ptyfs_free_get(ptyfstype type, int pty, struct proc *p) +{ + struct ptyfs_hashhead *ppp; + struct ptyfsnode *pp; + + simple_lock(&ptyfs_free_slock); + ppp = &ptyfs_free_tbl[PTYHASH(type, pty, ptyfs_free_mask)]; + LIST_FOREACH(pp, ppp, ptyfs_hash) { + if (pty == pp->ptyfs_pty && pp->ptyfs_type == type) { + LIST_REMOVE(pp, ptyfs_hash); + simple_unlock(&ptyfs_free_slock); + return pp; + } + } + simple_unlock(&ptyfs_free_slock); + + MALLOC(pp, void *, sizeof(struct ptyfsnode), M_TEMP, M_WAITOK); + pp->ptyfs_pty = pty; + pp->ptyfs_type = type; + pp->ptyfs_fileno = PTYFS_FILENO(pty, type); + ptyfs_getinfo(pp, p); + return pp; +} + +struct vnode * +ptyfs_used_get(ptyfstype type, int pty, struct mount *mp) +{ + struct ptyfs_hashhead *ppp; + struct ptyfsnode *pp; + struct vnode *vp; + +loop: + simple_lock(&ptyfs_used_slock); + ppp = &ptyfs_used_tbl[PTYHASH(type, pty, ptyfs_used_mask)]; + LIST_FOREACH(pp, ppp, ptyfs_hash) { + vp = PTYFSTOV(pp); + if (pty == pp->ptyfs_pty && pp->ptyfs_type == type && + vp->v_mount == mp) { + simple_lock(&vp->v_interlock); + simple_unlock(&ptyfs_used_slock); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) + goto loop; + return vp; + } + } + simple_unlock(&ptyfs_used_slock); + return NULL; +} + +/* + * Insert the ptyfsnode into the used table and lock it. + */ +static void +ptyfs_hashins(struct ptyfsnode *pp) +{ + struct ptyfs_hashhead *ppp; + + /* lock the ptyfsnode, then put it on the appropriate hash list */ + lockmgr(&pp->ptyfs_vnode->v_lock, LK_EXCLUSIVE, NULL); + + simple_lock(&ptyfs_used_slock); + ppp = &ptyfs_used_tbl[PTYHASH(pp->ptyfs_type, pp->ptyfs_pty, + ptyfs_used_mask)]; + LIST_INSERT_HEAD(ppp, pp, ptyfs_hash); + simple_unlock(&ptyfs_used_slock); +} + +/* + * Remove the ptyfsnode from the used table, and add it to the free table + */ +static void +ptyfs_hashrem(struct ptyfsnode *pp) +{ + struct ptyfs_hashhead *ppp; + + simple_lock(&ptyfs_used_slock); + LIST_REMOVE(pp, ptyfs_hash); + simple_unlock(&ptyfs_used_slock); + + simple_lock(&ptyfs_free_slock); + ppp = &ptyfs_free_tbl[PTYHASH(pp->ptyfs_type, pp->ptyfs_pty, + ptyfs_free_mask)]; + LIST_INSERT_HEAD(ppp, pp, ptyfs_hash); + simple_unlock(&ptyfs_free_slock); +} diff --git a/sys/miscfs/ptyfs/ptyfs_vfsops.c b/sys/miscfs/ptyfs/ptyfs_vfsops.c new file mode 100644 index 000000000000..5315ea258103 --- /dev/null +++ b/sys/miscfs/ptyfs/ptyfs_vfsops.c @@ -0,0 +1,354 @@ +/* $NetBSD: ptyfs_vfsops.c,v 1.1 2004/11/11 01:40:32 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software donated 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. + * + */ + +/* + * Kernel params Filesystem + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: ptyfs_vfsops.c,v 1.1 2004/11/11 01:40:32 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MALLOC_DEFINE(M_PTYFSMNT, "ptyfs mount", "ptyfs mount structures"); + +void ptyfs_init(void); +void ptyfs_reinit(void); +void ptyfs_done(void); +int ptyfs_mount(struct mount *, const char *, void *, struct nameidata *, + struct proc *); +int ptyfs_start(struct mount *, int, struct proc *); +int ptyfs_unmount(struct mount *, int, struct proc *); +int ptyfs_statvfs(struct mount *, struct statvfs *, struct proc *); +int ptyfs_quotactl(struct mount *, int, uid_t, void *, struct proc *); +int ptyfs_sync(struct mount *, int, struct ucred *, struct proc *); +int ptyfs_vget(struct mount *, ino_t, struct vnode **); +int ptyfs_fhtovp(struct mount *, struct fid *, struct vnode **); +int ptyfs_checkexp(struct mount *, struct mbuf *, int *, struct ucred **); +int ptyfs_vptofh(struct vnode *, struct fid *); + +static int ptyfs__allocvp(struct ptm_pty *, struct proc *, struct vnode **, + dev_t, char); +static int ptyfs__makename(struct ptm_pty *, char *, size_t, dev_t, char); + +/* + * ptm glue: When we mount, we make ptm point to us. + */ +struct ptm_pty *ptyfs_save_ptm; + +struct ptm_pty ptm_ptyfspty = { + ptyfs__allocvp, + ptyfs__makename, + NULL +}; + +static int +ptyfs__makename(struct ptm_pty *pt, char *buf, size_t bufsiz, dev_t dev, + char ms) +{ + struct mount *mp = pt->arg; + size_t len; + + switch (ms) { + case 'p': + /* We don't provide access to the master, should we? */ + len = snprintf(buf, bufsiz, "/dev/null"); + break; + case 't': + len = snprintf(buf, bufsiz, "%s/%d", mp->mnt_stat.f_mntonname, + minor(dev)); + break; + default: + return EINVAL; + } + + return len >= bufsiz ? ENOSPC : 0; +} + +static int +/*ARGSUSED*/ +ptyfs__allocvp(struct ptm_pty *pt, struct proc *p, struct vnode **vpp, + dev_t dev, char ms) +{ + struct mount *mp = pt->arg; + ptyfstype type; + + switch (ms) { + case 'p': + type = PTYFSptc; + break; + case 't': + type = PTYFSpts; + break; + default: + return EINVAL; + } + + return ptyfs_allocvp(mp, vpp, type, minor(dev), p); +} + +void +ptyfs_init(void) +{ +#ifdef _LKM + malloc_type_attach(M_PTYFSMNT); +#endif + ptyfs_hashinit(); +} + +void +ptyfs_reinit(void) +{ + ptyfs_hashreinit(); +} + +void +ptyfs_done(void) +{ +#ifdef _LKM + malloc_type_detach(M_PTYFSMNT); +#endif + ptyfs_hashdone(); +} + +/* + * Mount the Pseudo tty params filesystem + */ +int +ptyfs_mount(struct mount *mp, const char *path, void *data, + struct nameidata *ndp, struct proc *p) +{ + int error = 0; + + if (UIO_MX & (UIO_MX - 1)) { + log(LOG_ERR, "ptyfs: invalid directory entry size"); + return EINVAL; + } + + if (mp->mnt_flag & MNT_GETARGS) + return 0; + /* + * Update is a no-op + */ + if (mp->mnt_flag & MNT_UPDATE) + return EOPNOTSUPP; + + mp->mnt_data = NULL; + mp->mnt_flag |= MNT_LOCAL; + vfs_getnewfsid(mp); + + if ((error = set_statvfs_info(path, UIO_USERSPACE, "ptyfs", + UIO_SYSSPACE, mp, p)) != 0) { + return error; + } + + /* Point pty access to us */ + if (ptyfs_save_ptm != NULL) + return EBUSY; + + ptm_ptyfspty.arg = mp; + ptyfs_save_ptm = pty_sethandler(&ptm_ptyfspty); + return 0; +} + +/*ARGSUSED*/ +int +ptyfs_start(struct mount *mp, int flags, struct proc *p) +{ + return 0; +} + +/*ARGSUSED*/ +int +ptyfs_unmount(struct mount *mp, int mntflags, struct proc *p) +{ + int error; + int flags = 0; + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + if ((error = vflush(mp, 0, flags)) != 0) + return (error); + + /* Restore where pty access was pointing */ + ptm_ptyfspty.arg = NULL; + (void)pty_sethandler(ptyfs_save_ptm); + ptyfs_save_ptm = NULL; + /* + * Finally, throw away the ptyfs_mount structure + */ + return 0; +} + +int +ptyfs_root(struct mount *mp, struct vnode **vpp) +{ + /* setup "." */ + return ptyfs_allocvp(mp, vpp, PTYFSroot, 0, NULL); +} + +/*ARGSUSED*/ +int +ptyfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, struct proc *p) +{ + return EOPNOTSUPP; +} + +/*ARGSUSED*/ +int +ptyfs_statvfs(struct mount *mp, struct statvfs *sbp, struct proc *p) +{ + sbp->f_bsize = DEV_BSIZE; + sbp->f_frsize = DEV_BSIZE; + sbp->f_iosize = DEV_BSIZE; + sbp->f_blocks = 2; /* 1K to keep df happy */ + sbp->f_bfree = 0; + sbp->f_bavail = 0; + sbp->f_bresvd = 0; + sbp->f_files = 1024; /* XXX lie */ + sbp->f_ffree = 128; /* XXX lie */ + sbp->f_favail = 128; /* XXX lie */ + sbp->f_fresvd = 0; + sbp->f_namemax = MAXNAMLEN; + copy_statvfs_info(sbp, mp); + return 0; +} + +/*ARGSUSED*/ +int +ptyfs_sync(struct mount *mp, int waitfor, struct ucred *uc, struct proc *p) +{ + return 0; +} + +/* + * Kernfs flat namespace lookup. + * Currently unsupported. + */ +/*ARGSUSED*/ +int +ptyfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) +{ + return EOPNOTSUPP; +} + +/*ARGSUSED*/ +int +ptyfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) +{ + return EOPNOTSUPP; +} + +/*ARGSUSED*/ +int +ptyfs_checkexp(struct mount *mp, struct mbuf *mb, int *wh, struct ucred **anon) +{ + return EOPNOTSUPP; +} + +SYSCTL_SETUP(sysctl_vfs_ptyfs_setup, "sysctl vfs.ptyfs subtree setup") +{ + + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "vfs", NULL, + NULL, 0, NULL, 0, + CTL_VFS, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "ptyfs", + SYSCTL_DESCR("Pty file system"), + NULL, 0, NULL, 0, + CTL_VFS, 23, CTL_EOL); + /* + * XXX the "23" above could be dynamic, thereby eliminating + * one more instance of the "number to vfs" mapping problem, + * but "23" is the order as taken from sys/mount.h + */ +} + + +/*ARGSUSED*/ +int +ptyfs_vptofh(struct vnode *vp, struct fid *fhp) +{ + return EOPNOTSUPP; +} + +extern const struct vnodeopv_desc ptyfs_vnodeop_opv_desc; + +const struct vnodeopv_desc * const ptyfs_vnodeopv_descs[] = { + &ptyfs_vnodeop_opv_desc, + NULL, +}; + +struct vfsops ptyfs_vfsops = { + MOUNT_PTYFS, + ptyfs_mount, + ptyfs_start, + ptyfs_unmount, + ptyfs_root, + ptyfs_quotactl, + ptyfs_statvfs, + ptyfs_sync, + ptyfs_vget, + ptyfs_fhtovp, + ptyfs_vptofh, + ptyfs_init, + ptyfs_reinit, + ptyfs_done, + NULL, + NULL, /* vfs_mountroot */ + ptyfs_checkexp, + (int (*)(struct mount *, struct vnode *, struct timespec *))eopnotsupp, + ptyfs_vnodeopv_descs, +}; diff --git a/sys/miscfs/ptyfs/ptyfs_vnops.c b/sys/miscfs/ptyfs/ptyfs_vnops.c new file mode 100644 index 000000000000..4df998496e23 --- /dev/null +++ b/sys/miscfs/ptyfs/ptyfs_vnops.c @@ -0,0 +1,1030 @@ +/* $NetBSD: ptyfs_vnops.c,v 1.1 2004/11/11 01:40:32 christos Exp $ */ + +/* + * 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 + */ + +/* + * ptyfs vnode interface + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: ptyfs_vnops.c,v 1.1 2004/11/11 01:40:32 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* for PAGE_SIZE */ + +#include + +#include +#include +#include + +/* + * Vnode Operations. + * + */ + +int ptyfs_lookup (void *); +#define ptyfs_create genfs_eopnotsupp +#define ptyfs_mknod genfs_eopnotsupp +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 *); +#define ptyfs_fcntl genfs_fcntl +int ptyfs_ioctl (void *); +int ptyfs_poll (void *); +int ptyfs_kqfilter (void *); +#define ptyfs_revoke genfs_revoke +#define ptyfs_mmap genfs_eopnotsupp +#define ptyfs_fsync genfs_nullop +#define ptyfs_seek genfs_nullop +#define ptyfs_remove genfs_eopnotsupp +#define ptyfs_link genfs_abortop +#define ptyfs_rename genfs_eopnotsupp +#define ptyfs_mkdir genfs_eopnotsupp +#define ptyfs_rmdir genfs_eopnotsupp +#define ptyfs_symlink genfs_abortop +int ptyfs_readdir (void *); +#define ptyfs_readlink genfs_eopnotsupp +#define ptyfs_abortop genfs_abortop +int ptyfs_inactive (void *); +int ptyfs_reclaim (void *); +#define ptyfs_lock genfs_lock +#define ptyfs_unlock genfs_unlock +#define ptyfs_bmap genfs_badop +#define ptyfs_strategy genfs_badop +int ptyfs_print (void *); +int ptyfs_pathconf (void *); +#define ptyfs_islocked genfs_islocked +#define ptyfs_advlock genfs_einval +#define ptyfs_blkatoff genfs_eopnotsupp +#define ptyfs_valloc genfs_eopnotsupp +#define ptyfs_vfree genfs_nullop +#define ptyfs_truncate genfs_eopnotsupp +int ptyfs_update (void *); +#define ptyfs_bwrite genfs_eopnotsupp +#define ptyfs_putpages genfs_null_putpages + +static int ptyfs_chown(struct vnode *, uid_t, gid_t, struct ucred *, + struct proc *); +static int ptyfs_chmod(struct vnode *, mode_t, struct ucred *, struct proc *); +static void ptyfs_time(struct ptyfsnode *, struct timespec *, + struct timespec *); +static int atoi(const char *, size_t); +static u_int digits(u_int); + +extern const struct cdevsw pts_cdevsw, ptc_cdevsw; + +/* + * ptyfs vnode operations. + */ +int (**ptyfs_vnodeop_p)(void *); +const struct vnodeopv_entry_desc ptyfs_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, ptyfs_lookup }, /* lookup */ + { &vop_create_desc, ptyfs_create }, /* create */ + { &vop_mknod_desc, ptyfs_mknod }, /* mknod */ + { &vop_open_desc, ptyfs_open }, /* open */ + { &vop_close_desc, ptyfs_close }, /* close */ + { &vop_access_desc, ptyfs_access }, /* access */ + { &vop_getattr_desc, ptyfs_getattr }, /* getattr */ + { &vop_setattr_desc, ptyfs_setattr }, /* setattr */ + { &vop_read_desc, ptyfs_read }, /* read */ + { &vop_write_desc, ptyfs_write }, /* write */ + { &vop_ioctl_desc, ptyfs_ioctl }, /* ioctl */ + { &vop_fcntl_desc, ptyfs_fcntl }, /* fcntl */ + { &vop_poll_desc, ptyfs_poll }, /* poll */ + { &vop_kqfilter_desc, ptyfs_kqfilter }, /* kqfilter */ + { &vop_revoke_desc, ptyfs_revoke }, /* revoke */ + { &vop_mmap_desc, ptyfs_mmap }, /* mmap */ + { &vop_fsync_desc, ptyfs_fsync }, /* fsync */ + { &vop_seek_desc, ptyfs_seek }, /* seek */ + { &vop_remove_desc, ptyfs_remove }, /* remove */ + { &vop_link_desc, ptyfs_link }, /* link */ + { &vop_rename_desc, ptyfs_rename }, /* rename */ + { &vop_mkdir_desc, ptyfs_mkdir }, /* mkdir */ + { &vop_rmdir_desc, ptyfs_rmdir }, /* rmdir */ + { &vop_symlink_desc, ptyfs_symlink }, /* symlink */ + { &vop_readdir_desc, ptyfs_readdir }, /* readdir */ + { &vop_readlink_desc, ptyfs_readlink }, /* readlink */ + { &vop_abortop_desc, ptyfs_abortop }, /* abortop */ + { &vop_inactive_desc, ptyfs_inactive }, /* inactive */ + { &vop_reclaim_desc, ptyfs_reclaim }, /* reclaim */ + { &vop_lock_desc, ptyfs_lock }, /* lock */ + { &vop_unlock_desc, ptyfs_unlock }, /* unlock */ + { &vop_bmap_desc, ptyfs_bmap }, /* bmap */ + { &vop_strategy_desc, ptyfs_strategy }, /* strategy */ + { &vop_print_desc, ptyfs_print }, /* print */ + { &vop_islocked_desc, ptyfs_islocked }, /* islocked */ + { &vop_pathconf_desc, ptyfs_pathconf }, /* pathconf */ + { &vop_advlock_desc, ptyfs_advlock }, /* advlock */ + { &vop_blkatoff_desc, ptyfs_blkatoff }, /* blkatoff */ + { &vop_valloc_desc, ptyfs_valloc }, /* valloc */ + { &vop_vfree_desc, ptyfs_vfree }, /* vfree */ + { &vop_truncate_desc, ptyfs_truncate }, /* truncate */ + { &vop_update_desc, ptyfs_update }, /* update */ + { &vop_bwrite_desc, ptyfs_bwrite }, /* bwrite */ + { &vop_putpages_desc, ptyfs_putpages }, /* putpages */ + { NULL, NULL } +}; +const struct vnodeopv_desc ptyfs_vnodeop_opv_desc = + { &ptyfs_vnodeop_p, ptyfs_vnodeop_entries }; + +/* + * _inactive is called when the ptyfsnode + * is vrele'd and the reference count goes + * to zero. (vp) will be on the vnode free + * list, so to get it back vget() must be + * used. + * + * for ptyfs, check if the pty is still + * in use and if it isn't then just throw away + * the vnode by calling vgone(). + * + * (vp) is locked on entry, but must be unlocked on exit. + */ +int +ptyfs_inactive(void *v) +{ + struct vop_inactive_args /* { + struct vnode *a_vp; + struct proc *a_p; + } */ *ap = v; + struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); + + VOP_UNLOCK(ap->a_vp, 0); + if (pty_isfree(ptyfs->ptyfs_pty, 1)) + vgone(ap->a_vp); + + return 0; +} + +/* + * _reclaim is called when getnewvnode() + * wants to make use of an entry on the vnode + * free list. at this time the filesystem needs + * to free any private data and remove the node + * from any private lists. + */ +int +ptyfs_reclaim(void *v) +{ + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap = v; + return ptyfs_freevp(ap->a_vp); +} + +/* + * Return POSIX pathconf information applicable to special devices. + */ +int +ptyfs_pathconf(void *v) +{ + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *ap = v; + + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = LINK_MAX; + return 0; + case _PC_MAX_CANON: + *ap->a_retval = MAX_CANON; + return 0; + case _PC_MAX_INPUT: + *ap->a_retval = MAX_INPUT; + return 0; + case _PC_PIPE_BUF: + *ap->a_retval = PIPE_BUF; + return 0; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return 0; + case _PC_VDISABLE: + *ap->a_retval = _POSIX_VDISABLE; + return 0; + case _PC_SYNC_IO: + *ap->a_retval = 1; + return 0; + default: + return EINVAL; + } +} + +/* + * _print is used for debugging. + * just print a readable description + * of (vp). + */ +int +ptyfs_print(void *v) +{ + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap = v; + struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); + + printf("tag VT_PTYFS, type %d, pty %d\n", + ptyfs->ptyfs_type, ptyfs->ptyfs_pty); + return 0; +} + +/* + * 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; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); + struct vattr *vap = ap->a_vap; + struct timespec ts; + + TIMEVAL_TO_TIMESPEC(&time, &ts); + ptyfs_time(ptyfs, &ts, &ts); + + /* 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_fileid = ptyfs->ptyfs_fileno; + vap->va_gen = 0; + vap->va_flags = 0; + vap->va_nlink = 1; + vap->va_blocksize = PAGE_SIZE; + + vap->va_atime = ptyfs->ptyfs_atime; + vap->va_mtime = ptyfs->ptyfs_mtime; + vap->va_ctime = ptyfs->ptyfs_ctime; + vap->va_birthtime = ptyfs->ptyfs_birthtime; + vap->va_mode = ptyfs->ptyfs_mode; + vap->va_flags = ptyfs->ptyfs_flags; + vap->va_uid = ptyfs->ptyfs_uid; + vap->va_gid = ptyfs->ptyfs_gid; + + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + case PTYFSptc: + if (pty_isfree(ptyfs->ptyfs_pty, 1)) + return ENOENT; + vap->va_bytes = vap->va_size = digits(ptyfs->ptyfs_pty); + vap->va_rdev = ap->a_vp->v_rdev; + break; + case PTYFSroot: + vap->va_rdev = 0; + vap->va_bytes = vap->va_size = DEV_BSIZE; + break; + + default: + return EOPNOTSUPP; + } + + return 0; +} + +/*ARGSUSED*/ +int +ptyfs_setattr(void *v) +{ + struct vop_setattr_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + struct vattr *vap = ap->a_vap; + struct ucred *cred = ap->a_cred; + struct proc *p = ap->a_p; + int error; + + if (vap->va_size != VNOVAL) { + switch (ptyfs->ptyfs_type) { + case PTYFSroot: + return EISDIR; + case PTYFSpts: + case PTYFSptc: + break; + default: + return EINVAL; + } + } + + if (vap->va_flags != VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + if (cred->cr_uid != ptyfs->ptyfs_uid && + (error = suser(cred, &p->p_acflag)) != 0) + return error; + if (cred->cr_uid == 0) { + if ((ptyfs->ptyfs_flags & (SF_IMMUTABLE | SF_APPEND)) && + securelevel > 0) + return EPERM; + /* Snapshot flag cannot be set or cleared */ + if ((vap->va_flags & SF_SNAPSHOT) != + (ptyfs->ptyfs_flags & SF_SNAPSHOT)) + return EPERM; + ptyfs->ptyfs_flags = vap->va_flags; + } else { + if ((ptyfs->ptyfs_flags & (SF_IMMUTABLE | SF_APPEND)) || + (vap->va_flags & UF_SETTABLE) != vap->va_flags) + return EPERM; + if ((ptyfs->ptyfs_flags & SF_SETTABLE) != + (vap->va_flags & SF_SETTABLE)) + return EPERM; + ptyfs->ptyfs_flags &= SF_SETTABLE; + ptyfs->ptyfs_flags |= (vap->va_flags & UF_SETTABLE); + } + ptyfs->ptyfs_flag |= PTYFS_CHANGE; + if (vap->va_flags & (IMMUTABLE | APPEND)) + return 0; + } + if (ptyfs->ptyfs_flags & (IMMUTABLE | APPEND)) + return EPERM; + /* + * 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; + if (ptyfs->ptyfs_type == PTYFSroot) + return EPERM; + error = ptyfs_chown(vp, vap->va_uid, vap->va_gid, cred, p); + 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; + if (cred->cr_uid != ptyfs->ptyfs_uid && + (error = suser(cred, &p->p_acflag)) && + ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0)) + return (error); + if (vap->va_atime.tv_sec != VNOVAL) + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) + ptyfs->ptyfs_flag |= PTYFS_ACCESS; + if (vap->va_mtime.tv_sec != VNOVAL) + ptyfs->ptyfs_flag |= PTYFS_CHANGE | PTYFS_MODIFY; + if (vap->va_birthtime.tv_sec != VNOVAL) + ptyfs->ptyfs_birthtime = vap->va_birthtime; + ptyfs->ptyfs_flag |= PTYFS_CHANGE; + error = VOP_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_type == PTYFSroot) + return EPERM; + 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, p); + if (error) + return error; + } + VN_KNOTE(vp, NOTE_ATTRIB); + return 0; +} + +/* + * Change the mode on a file. + * Inode must be locked before calling. + */ +static int +ptyfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct proc *p) +{ + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + int error; + + if (cred->cr_uid != ptyfs->ptyfs_uid && + (error = suser(cred, &p->p_acflag)) != 0) + return error; + ptyfs->ptyfs_mode &= ~ALLPERMS; + ptyfs->ptyfs_mode |= (mode & ALLPERMS); + return 0; +} + +/* + * Perform chown operation on inode ip; + * inode must be locked prior to call. + */ +static int +ptyfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, + struct proc *p) +{ + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + int error; + + if (uid == (uid_t)VNOVAL) + uid = ptyfs->ptyfs_uid; + if (gid == (gid_t)VNOVAL) + gid = ptyfs->ptyfs_gid; + /* + * If we don't own the file, are trying to change the owner + * of the file, or are not a member of the target group, + * the caller's credentials must imply super-user privilege + * or the call fails. + */ + if ((cred->cr_uid != ptyfs->ptyfs_uid || uid != ptyfs->ptyfs_uid || + (gid != ptyfs->ptyfs_gid && + !(cred->cr_gid == gid || groupmember((gid_t)gid, cred)))) && + ((error = suser(cred, &p->p_acflag)) != 0)) + return error; + + ptyfs->ptyfs_gid = gid; + ptyfs->ptyfs_uid = uid; + return 0; +} + +/* + * 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; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + struct vattr va; + int error; + + if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred, ap->a_p)) != 0) + return error; + + return vaccess(va.va_type, va.va_mode, + va.va_uid, va.va_gid, ap->a_mode, ap->a_cred); +} + +/* + * 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, and we're on the last component and the caller requested the + * parent locked, try to re-lock the parent. We do this to prevent lock + * races. + * + * For anything else, get the needed node. Then unlock the parent if not + * the last component or not LOCKPARENT (i.e. if we wouldn't re-lock the + * parent in the .. case). + * + * We try to exit with the parent locked in error cases. + */ +int +ptyfs_lookup(void *v) +{ + struct vop_lookup_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, wantpunlock; + + *vpp = NULL; + cnp->cn_flags &= ~PDIRUNLOCK; + + if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) + return EROFS; + + if (cnp->cn_namelen == 1 && *pname == '.') { + *vpp = dvp; + VREF(dvp); + return 0; + } + + wantpunlock = ~cnp->cn_flags & (LOCKPARENT | ISLASTCN); + 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; + + pty = atoi(pname, cnp->cn_namelen); + + if (pty < 0 || pty >= npty || pty_isfree(pty, 1)) + break; + + error = ptyfs_allocvp(dvp->v_mount, vpp, PTYFSpts, pty, + curproc); + if (error == 0 && wantpunlock) { + VOP_UNLOCK(dvp, 0); + cnp->cn_flags |= PDIRUNLOCK; + } + return error; + + default: + return ENOTDIR; + } + + return cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS; +} + +/* + * 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 efficent + * 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; + struct ucred *a_cred; + int *a_eofflag; + off_t **a_cookies; + int *a_ncookies; + } */ *ap = v; + struct uio *uio = ap->a_uio; + struct dirent d; + struct ptyfsnode *ptyfs; + off_t i; + int error; + off_t *cookies = NULL; + int ncookies; + struct vnode *vp; + int nc = 0; + + vp = ap->a_vp; + ptyfs = VTOPTYFS(vp); + + if (uio->uio_resid < UIO_MX) + return EINVAL; + if (uio->uio_offset < 0) + return EINVAL; + + error = 0; + i = uio->uio_offset; + (void)memset(&d, 0, sizeof(d)); + d.d_reclen = UIO_MX; + ncookies = uio->uio_resid / UIO_MX; + + switch (ptyfs->ptyfs_type) { + case PTYFSroot: /* root */ + + if (i >= npty) + return 0; + + if (ap->a_ncookies) { + ncookies = min(ncookies, (npty + 2 - i)); + cookies = malloc(ncookies * sizeof (off_t), + M_TEMP, M_WAITOK); + *ap->a_cookies = cookies; + } + + for (; i < 2; i++) { + switch (i) { + case 0: /* `.' */ + case 1: /* `..' */ + d.d_fileno = PTYFS_FILENO(0, PTYFSroot); + d.d_namlen = i + 1; + (void)memcpy(d.d_name, "..", d.d_namlen); + d.d_name[i + 1] = '\0'; + d.d_type = DT_DIR; + break; + } + if ((error = uiomove(&d, UIO_MX, uio)) != 0) + break; + if (cookies) + *cookies++ = i + 1; + nc++; + } + if (error) { + ncookies = nc; + break; + } + for (; uio->uio_resid >= UIO_MX && i < npty; i++) { + /* check for used ptys */ + if (pty_isfree(i - 2, 1)) + continue; + + d.d_fileno = PTYFS_FILENO(i - 2, PTYFSpts); + d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), + "%lld", (long long)(i - 2)); + d.d_type = DT_CHR; + if ((error = uiomove(&d, UIO_MX, uio)) != 0) + break; + if (cookies) + *cookies++ = i + 1; + nc++; + } + ncookies = nc; + break; + + default: + error = ENOTDIR; + break; + } + + if (ap->a_ncookies) { + if (error) { + if (cookies) + free(*ap->a_cookies, M_TEMP); + *ap->a_ncookies = 0; + *ap->a_cookies = NULL; + } else + *ap->a_ncookies = ncookies; + } + uio->uio_offset = i; + return error; +} + +int +ptyfs_open(void *v) +{ + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + + ptyfs->ptyfs_flag |= PTYFS_CHANGE|PTYFS_ACCESS; + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + case PTYFSptc: + return spec_open(v); + case PTYFSroot: + return 0; + default: + return EINVAL; + } +} + +int +ptyfs_close(void *v) +{ + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + struct timespec ts; + ptyfs_time(ptyfs, &ts, &ts); + + simple_lock(&vp->v_interlock); + if (vp->v_usecount > 1) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + ptyfs_time(ptyfs, &ts, &ts); + } + simple_unlock(&vp->v_interlock); + + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + case PTYFSptc: + return spec_close(v); + case PTYFSroot: + return 0; + default: + return EINVAL; + } +} + +int +ptyfs_read(void *v) +{ + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + int error; + + ptyfs->ptyfs_flag |= PTYFS_ACCESS; + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + VOP_UNLOCK(vp, 0); + error = (*pts_cdevsw.d_read)(vp->v_rdev, ap->a_uio, + ap->a_ioflag); + vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); + return error; + case PTYFSptc: + VOP_UNLOCK(vp, 0); + error = (*ptc_cdevsw.d_read)(vp->v_rdev, ap->a_uio, + ap->a_ioflag); + vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); + return error; + default: + return EOPNOTSUPP; + } +} + +int +ptyfs_write(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap = v; + int error; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + + ptyfs->ptyfs_flag |= PTYFS_MODIFY; + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + VOP_UNLOCK(vp, 0); + error = (*pts_cdevsw.d_write)(vp->v_rdev, ap->a_uio, + ap->a_ioflag); + vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); + return error; + case PTYFSptc: + VOP_UNLOCK(vp, 0); + error = (*ptc_cdevsw.d_write)(vp->v_rdev, ap->a_uio, + ap->a_ioflag); + vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); + return error; + default: + return EOPNOTSUPP; + } +} + +int +ptyfs_ioctl(void *v) +{ + struct vop_ioctl_args /* { + struct vnode *a_vp; + u_long a_command; + void *a_data; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + return (*pts_cdevsw.d_ioctl)(vp->v_rdev, ap->a_command, + ap->a_data, ap->a_fflag, ap->a_p); + case PTYFSptc: + return (*ptc_cdevsw.d_ioctl)(vp->v_rdev, ap->a_command, + ap->a_data, ap->a_fflag, ap->a_p); + default: + return EOPNOTSUPP; + } +} + +int +ptyfs_poll(void *v) +{ + struct vop_poll_args /* { + struct vnode *a_vp; + int a_events; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + return (*pts_cdevsw.d_poll)(vp->v_rdev, ap->a_events, ap->a_p); + case PTYFSptc: + return (*ptc_cdevsw.d_poll)(vp->v_rdev, ap->a_events, ap->a_p); + default: + return genfs_poll(v); + } +} + +int +ptyfs_kqfilter(void *v) +{ + struct vop_kqfilter_args /* { + struct vnode *a_vp; + struct knote *a_kn; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct ptyfsnode *ptyfs = VTOPTYFS(vp); + + switch (ptyfs->ptyfs_type) { + case PTYFSpts: + return (*pts_cdevsw.d_kqfilter)(vp->v_rdev, ap->a_kn); + case PTYFSptc: + return (*ptc_cdevsw.d_kqfilter)(vp->v_rdev, ap->a_kn); + default: + return genfs_kqfilter(v); + } +} + +int +ptyfs_update(v) + void *v; +{ + struct vop_update_args /* { + struct vnode *a_vp; + struct timespec *a_access; + struct timespec *a_modify; + int a_flags; + } */ *ap = v; + struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); + struct timespec ts; + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return 0; + + TIMEVAL_TO_TIMESPEC(&time, &ts); + if (ap->a_access == NULL) + ap->a_access = &ts; + if (ap->a_modify == NULL) + ap->a_modify = &ts; + ptyfs_time(ptyfs, ap->a_access, ap->a_modify); + return 0; +} + +static void +ptyfs_time(struct ptyfsnode *ptyfs, struct timespec *atime, + struct timespec *mtime) +{ + if (ptyfs->ptyfs_flag & PTYFS_MODIFY) { + ptyfs->ptyfs_mtime = *mtime; + ptyfs->ptyfs_atime = *atime; + } else if (ptyfs->ptyfs_flag & PTYFS_ACCESS) + ptyfs->ptyfs_atime = *atime; + if (ptyfs->ptyfs_flag & PTYFS_CHANGE) + ptyfs->ptyfs_ctime = *atime; + ptyfs->ptyfs_flag = 0; +} + +/* + * convert decimal ascii to int + */ +static int +atoi(const char *b, size_t len) +{ + int p = 0; + + while (len--) { + char c = *b++; + if (c < '0' || c > '9') + return -1; + p = 10 * p + (c - '0'); + } + + return p; +} + +/* + * Return the number of decimal digits + */ +static u_int +digits(u_int i) +{ + u_int d; + for (d = 1; i != 0; d++) + i /= 10; + return d; +}