Import 4.4BSD-Lite2 onto the vendor branch, note that in the kernel, all

files are off the vendor branch, so this should not change anything.

A "U" marker generally means that the file was not changed in between
the 4.4Lite and Lite-2 releases, and does not need a merge.  "C" generally
means that there was a change.
[two new auxillary files in miscfs/union]
This commit is contained in:
Peter Wemm 1996-03-11 19:29:25 +00:00
parent 91b6e176b3
commit 5e5861b9c6
38 changed files with 15178 additions and 0 deletions

View file

@ -0,0 +1,356 @@
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)dead_vnops.c 8.3 (Berkeley) 5/14/95
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/errno.h>
#include <sys/namei.h>
#include <sys/buf.h>
/*
* Prototypes for dead operations on vnodes.
*/
int dead_badop(),
dead_ebadf();
int dead_lookup __P((struct vop_lookup_args *));
#define dead_create ((int (*) __P((struct vop_create_args *)))dead_badop)
#define dead_mknod ((int (*) __P((struct vop_mknod_args *)))dead_badop)
int dead_open __P((struct vop_open_args *));
#define dead_close ((int (*) __P((struct vop_close_args *)))nullop)
#define dead_access ((int (*) __P((struct vop_access_args *)))dead_ebadf)
#define dead_getattr ((int (*) __P((struct vop_getattr_args *)))dead_ebadf)
#define dead_setattr ((int (*) __P((struct vop_setattr_args *)))dead_ebadf)
int dead_read __P((struct vop_read_args *));
int dead_write __P((struct vop_write_args *));
int dead_ioctl __P((struct vop_ioctl_args *));
int dead_select __P((struct vop_select_args *));
#define dead_mmap ((int (*) __P((struct vop_mmap_args *)))dead_badop)
#define dead_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
#define dead_seek ((int (*) __P((struct vop_seek_args *)))nullop)
#define dead_remove ((int (*) __P((struct vop_remove_args *)))dead_badop)
#define dead_link ((int (*) __P((struct vop_link_args *)))dead_badop)
#define dead_rename ((int (*) __P((struct vop_rename_args *)))dead_badop)
#define dead_mkdir ((int (*) __P((struct vop_mkdir_args *)))dead_badop)
#define dead_rmdir ((int (*) __P((struct vop_rmdir_args *)))dead_badop)
#define dead_symlink ((int (*) __P((struct vop_symlink_args *)))dead_badop)
#define dead_readdir ((int (*) __P((struct vop_readdir_args *)))dead_ebadf)
#define dead_readlink ((int (*) __P((struct vop_readlink_args *)))dead_ebadf)
#define dead_abortop ((int (*) __P((struct vop_abortop_args *)))dead_badop)
#define dead_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
#define dead_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
int dead_lock __P((struct vop_lock_args *));
#define dead_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
int dead_bmap __P((struct vop_bmap_args *));
int dead_strategy __P((struct vop_strategy_args *));
int dead_print __P((struct vop_print_args *));
#define dead_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked)
#define dead_pathconf ((int (*) __P((struct vop_pathconf_args *)))dead_ebadf)
#define dead_advlock ((int (*) __P((struct vop_advlock_args *)))dead_ebadf)
#define dead_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))dead_badop)
#define dead_valloc ((int (*) __P((struct vop_valloc_args *)))dead_badop)
#define dead_vfree ((int (*) __P((struct vop_vfree_args *)))dead_badop)
#define dead_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
#define dead_update ((int (*) __P((struct vop_update_args *)))nullop)
#define dead_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
int (**dead_vnodeop_p)();
struct vnodeopv_entry_desc dead_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, dead_lookup }, /* lookup */
{ &vop_create_desc, dead_create }, /* create */
{ &vop_mknod_desc, dead_mknod }, /* mknod */
{ &vop_open_desc, dead_open }, /* open */
{ &vop_close_desc, dead_close }, /* close */
{ &vop_access_desc, dead_access }, /* access */
{ &vop_getattr_desc, dead_getattr }, /* getattr */
{ &vop_setattr_desc, dead_setattr }, /* setattr */
{ &vop_read_desc, dead_read }, /* read */
{ &vop_write_desc, dead_write }, /* write */
{ &vop_ioctl_desc, dead_ioctl }, /* ioctl */
{ &vop_select_desc, dead_select }, /* select */
{ &vop_mmap_desc, dead_mmap }, /* mmap */
{ &vop_fsync_desc, dead_fsync }, /* fsync */
{ &vop_seek_desc, dead_seek }, /* seek */
{ &vop_remove_desc, dead_remove }, /* remove */
{ &vop_link_desc, dead_link }, /* link */
{ &vop_rename_desc, dead_rename }, /* rename */
{ &vop_mkdir_desc, dead_mkdir }, /* mkdir */
{ &vop_rmdir_desc, dead_rmdir }, /* rmdir */
{ &vop_symlink_desc, dead_symlink }, /* symlink */
{ &vop_readdir_desc, dead_readdir }, /* readdir */
{ &vop_readlink_desc, dead_readlink }, /* readlink */
{ &vop_abortop_desc, dead_abortop }, /* abortop */
{ &vop_inactive_desc, dead_inactive }, /* inactive */
{ &vop_reclaim_desc, dead_reclaim }, /* reclaim */
{ &vop_lock_desc, dead_lock }, /* lock */
{ &vop_unlock_desc, dead_unlock }, /* unlock */
{ &vop_bmap_desc, dead_bmap }, /* bmap */
{ &vop_strategy_desc, dead_strategy }, /* strategy */
{ &vop_print_desc, dead_print }, /* print */
{ &vop_islocked_desc, dead_islocked }, /* islocked */
{ &vop_pathconf_desc, dead_pathconf }, /* pathconf */
{ &vop_advlock_desc, dead_advlock }, /* advlock */
{ &vop_blkatoff_desc, dead_blkatoff }, /* blkatoff */
{ &vop_valloc_desc, dead_valloc }, /* valloc */
{ &vop_vfree_desc, dead_vfree }, /* vfree */
{ &vop_truncate_desc, dead_truncate }, /* truncate */
{ &vop_update_desc, dead_update }, /* update */
{ &vop_bwrite_desc, dead_bwrite }, /* bwrite */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc dead_vnodeop_opv_desc =
{ &dead_vnodeop_p, dead_vnodeop_entries };
/*
* Trivial lookup routine that always fails.
*/
/* ARGSUSED */
int
dead_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
*ap->a_vpp = NULL;
return (ENOTDIR);
}
/*
* Open always fails as if device did not exist.
*/
/* ARGSUSED */
dead_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
return (ENXIO);
}
/*
* Vnode op for read
*/
/* ARGSUSED */
dead_read(ap)
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
if (chkvnlock(ap->a_vp))
panic("dead_read: lock");
/*
* Return EOF for tty devices, EIO for others
*/
if ((ap->a_vp->v_flag & VISTTY) == 0)
return (EIO);
return (0);
}
/*
* Vnode op for write
*/
/* ARGSUSED */
dead_write(ap)
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
if (chkvnlock(ap->a_vp))
panic("dead_write: lock");
return (EIO);
}
/*
* Device ioctl operation.
*/
/* ARGSUSED */
dead_ioctl(ap)
struct vop_ioctl_args /* {
struct vnode *a_vp;
int a_command;
caddr_t a_data;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
if (!chkvnlock(ap->a_vp))
return (EBADF);
return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap));
}
/* ARGSUSED */
dead_select(ap)
struct vop_select_args /* {
struct vnode *a_vp;
int a_which;
int a_fflags;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
/*
* Let the user find out that the descriptor is gone.
*/
return (1);
}
/*
* Just call the device strategy routine
*/
dead_strategy(ap)
struct vop_strategy_args /* {
struct buf *a_bp;
} */ *ap;
{
if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) {
ap->a_bp->b_flags |= B_ERROR;
biodone(ap->a_bp);
return (EIO);
}
return (VOP_STRATEGY(ap->a_bp));
}
/*
* Wait until the vnode has finished changing state.
*/
dead_lock(ap)
struct vop_lock_args /* {
struct vnode *a_vp;
int a_flags;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
/*
* Since we are not using the lock manager, we must clear
* the interlock here.
*/
if (ap->a_flags & LK_INTERLOCK) {
simple_unlock(&vp->v_interlock);
ap->a_flags &= ~LK_INTERLOCK;
}
if (!chkvnlock(vp))
return (0);
return (VCALL(vp, VOFFSET(vop_lock), ap));
}
/*
* Wait until the vnode has finished changing state.
*/
dead_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct vnode **a_vpp;
daddr_t *a_bnp;
int *a_runp;
} */ *ap;
{
if (!chkvnlock(ap->a_vp))
return (EIO);
return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp));
}
/*
* Print out the contents of a dead vnode.
*/
/* ARGSUSED */
dead_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
printf("tag VT_NON, dead vnode\n");
}
/*
* Empty vnode failed operation
*/
dead_ebadf()
{
return (EBADF);
}
/*
* Empty vnode bad operation
*/
dead_badop()
{
panic("dead_badop called");
/* NOTREACHED */
}
/*
* We have to wait during times when the vnode is
* in a state of change.
*/
chkvnlock(vp)
register struct vnode *vp;
{
int locked = 0;
while (vp->v_flag & VXLOCK) {
vp->v_flag |= VXWANT;
sleep((caddr_t)vp, PINOD);
locked = 1;
}
return (locked);
}

81
sys/miscfs/fdesc/fdesc.h Normal file
View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)fdesc.h 8.8 (Berkeley) 4/3/95
*
* $Id: fdesc.h,v 1.8 1993/04/06 15:28:33 jsp Exp $
*/
#ifdef KERNEL
struct fdescmount {
struct vnode *f_root; /* Root node */
};
#define FD_ROOT 2
#define FD_DEVFD 3
#define FD_STDIN 4
#define FD_STDOUT 5
#define FD_STDERR 6
#define FD_CTTY 7
#define FD_DESC 8
#define FD_MAX 12
typedef enum {
Froot,
Fdevfd,
Fdesc,
Flink,
Fctty
} fdntype;
struct fdescnode {
LIST_ENTRY(fdescnode) fd_hash; /* Hash list */
struct vnode *fd_vnode; /* Back ptr to vnode */
fdntype fd_type; /* Type of this node */
unsigned fd_fd; /* Fd to be dup'ed */
char *fd_link; /* Link to fd/n */
int fd_ix; /* filesystem index */
};
#define VFSTOFDESC(mp) ((struct fdescmount *)((mp)->mnt_data))
#define VTOFDESC(vp) ((struct fdescnode *)(vp)->v_data)
extern dev_t devctty;
extern int fdesc_init __P((struct vfsconf *));
extern int fdesc_root __P((struct mount *, struct vnode **));
extern int fdesc_allocvp __P((fdntype, int, struct mount *, struct vnode **));
extern int (**fdesc_vnodeop_p)();
extern struct vfsops fdesc_vfsops;
#endif /* KERNEL */

View file

@ -0,0 +1,250 @@
/*
* 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. 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.
*
* @(#)fdesc_vfsops.c 8.10 (Berkeley) 5/14/95
*
* $Id: fdesc_vfsops.c,v 1.9 1993/04/06 15:28:33 jsp Exp $
*/
/*
* /dev/fd Filesystem
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <miscfs/fdesc/fdesc.h>
/*
* Mount the per-process file descriptors (/dev/fd)
*/
int
fdesc_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
int error = 0;
u_int size;
struct fdescmount *fmp;
struct vnode *rvp;
/*
* Update is a no-op
*/
if (mp->mnt_flag & MNT_UPDATE)
return (EOPNOTSUPP);
error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp);
if (error)
return (error);
MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount),
M_UFSMNT, M_WAITOK); /* XXX */
rvp->v_type = VDIR;
rvp->v_flag |= VROOT;
fmp->f_root = rvp;
/* XXX -- don't mark as local to work around fts() problems */
/*mp->mnt_flag |= MNT_LOCAL;*/
mp->mnt_data = (qaddr_t) fmp;
vfs_getnewfsid(mp);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
bcopy("fdesc", mp->mnt_stat.f_mntfromname, sizeof("fdesc"));
return (0);
}
int
fdesc_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
}
int
fdesc_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
int error;
int flags = 0;
struct vnode *rootvp = VFSTOFDESC(mp)->f_root;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
/*
* Clear out buffer cache. I don't think we
* ever get anything cached at this level at the
* moment, but who knows...
*/
if (rootvp->v_usecount > 1)
return (EBUSY);
if (error = vflush(mp, rootvp, flags))
return (error);
/*
* Release reference on underlying root vnode
*/
vrele(rootvp);
/*
* And blow it away for future re-use
*/
vgone(rootvp);
/*
* Finally, throw away the fdescmount structure
*/
free(mp->mnt_data, M_UFSMNT); /* XXX */
mp->mnt_data = 0;
return (0);
}
int
fdesc_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct vnode *vp;
/*
* Return locked reference to root.
*/
vp = VFSTOFDESC(mp)->f_root;
VREF(vp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
*vpp = vp;
return (0);
}
int
fdesc_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
struct filedesc *fdp;
int lim;
int i;
int last;
int freefd;
/*
* Compute number of free file descriptors.
* [ Strange results will ensue if the open file
* limit is ever reduced below the current number
* of open files... ]
*/
lim = p->p_rlimit[RLIMIT_NOFILE].rlim_cur;
fdp = p->p_fd;
last = min(fdp->fd_nfiles, lim);
freefd = 0;
for (i = fdp->fd_freefile; i < last; i++)
if (fdp->fd_ofiles[i] == NULL)
freefd++;
/*
* Adjust for the fact that the fdesc array may not
* have been fully allocated yet.
*/
if (fdp->fd_nfiles < lim)
freefd += (lim - fdp->fd_nfiles);
sbp->f_flags = 0;
sbp->f_bsize = 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_files = lim + 1; /* Allow for "." */
sbp->f_ffree = freefd; /* See comments above */
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
int
fdesc_sync(mp, waitfor)
struct mount *mp;
int waitfor;
{
return (0);
}
#define fdesc_fhtovp ((int (*) __P((struct mount *, struct fid *, \
struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
#define fdesc_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
struct proc *)))eopnotsupp)
#define fdesc_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
#define fdesc_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
eopnotsupp)
#define fdesc_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
struct vfsops fdesc_vfsops = {
fdesc_mount,
fdesc_start,
fdesc_unmount,
fdesc_root,
fdesc_quotactl,
fdesc_statfs,
fdesc_sync,
fdesc_vget,
fdesc_fhtovp,
fdesc_vptofh,
fdesc_init,
fdesc_sysctl,
};

View file

@ -0,0 +1,951 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)fdesc_vnops.c 8.17 (Berkeley) 5/22/95
*
* $Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp $
*/
/*
* /dev/fd Filesystem
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/kernel.h> /* boottime */
#include <sys/resourcevar.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/buf.h>
#include <sys/dirent.h>
#include <miscfs/fdesc/fdesc.h>
#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
#define FDL_WANT 0x01
#define FDL_LOCKED 0x02
static int fdcache_lock;
dev_t devctty;
#if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
#endif
#define NFDCACHE 4
#define FD_NHASH(ix) \
(&fdhashtbl[(ix) & fdhash])
LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
u_long fdhash;
/*
* Initialise cache headers
*/
fdesc_init(vfsp)
struct vfsconf *vfsp;
{
devctty = makedev(nchrdev, 0);
fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
}
int
fdesc_allocvp(ftype, ix, mp, vpp)
fdntype ftype;
int ix;
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct fdhashhead *fc;
struct fdescnode *fd;
int error = 0;
fc = FD_NHASH(ix);
loop:
for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) {
if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
if (vget(fd->fd_vnode, 0, p))
goto loop;
*vpp = fd->fd_vnode;
return (error);
}
}
/*
* otherwise lock the array while we call getnewvnode
* since that can block.
*/
if (fdcache_lock & FDL_LOCKED) {
fdcache_lock |= FDL_WANT;
sleep((caddr_t) &fdcache_lock, PINOD);
goto loop;
}
fdcache_lock |= FDL_LOCKED;
error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
if (error)
goto out;
MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
(*vpp)->v_data = fd;
fd->fd_vnode = *vpp;
fd->fd_type = ftype;
fd->fd_fd = -1;
fd->fd_link = 0;
fd->fd_ix = ix;
LIST_INSERT_HEAD(fc, fd, fd_hash);
out:;
fdcache_lock &= ~FDL_LOCKED;
if (fdcache_lock & FDL_WANT) {
fdcache_lock &= ~FDL_WANT;
wakeup((caddr_t) &fdcache_lock);
}
return (error);
}
/*
* vp is the current namei directory
* ndp is the name to locate in that directory...
*/
int
fdesc_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
char *pname = cnp->cn_nameptr;
struct proc *p = cnp->cn_proc;
int nfiles = p->p_fd->fd_nfiles;
unsigned fd;
int error;
struct vnode *fvp;
char *ln;
VOP_UNLOCK(dvp, 0, p);
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
VREF(dvp);
vn_lock(dvp, LK_SHARED | LK_RETRY, p);
return (0);
}
switch (VTOFDESC(dvp)->fd_type) {
default:
case Flink:
case Fdesc:
case Fctty:
error = ENOTDIR;
goto bad;
case Froot:
if (cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) {
error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
if (error)
goto bad;
*vpp = fvp;
fvp->v_type = VDIR;
vn_lock(fvp, LK_SHARED | LK_RETRY, p);
return (0);
}
if (cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) {
struct vnode *ttyvp = cttyvp(p);
if (ttyvp == NULL) {
error = ENXIO;
goto bad;
}
error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
if (error)
goto bad;
*vpp = fvp;
fvp->v_type = VFIFO;
vn_lock(fvp, LK_SHARED | LK_RETRY, p);
return (0);
}
ln = 0;
switch (cnp->cn_namelen) {
case 5:
if (bcmp(pname, "stdin", 5) == 0) {
ln = "fd/0";
fd = FD_STDIN;
}
break;
case 6:
if (bcmp(pname, "stdout", 6) == 0) {
ln = "fd/1";
fd = FD_STDOUT;
} else
if (bcmp(pname, "stderr", 6) == 0) {
ln = "fd/2";
fd = FD_STDERR;
}
break;
}
if (ln) {
error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
if (error)
goto bad;
VTOFDESC(fvp)->fd_link = ln;
*vpp = fvp;
fvp->v_type = VLNK;
vn_lock(fvp, LK_SHARED | LK_RETRY, p);
return (0);
} else {
error = ENOENT;
goto bad;
}
/* FALL THROUGH */
case Fdevfd:
if (cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) {
if (error = fdesc_root(dvp->v_mount, vpp))
goto bad;
return (0);
}
fd = 0;
while (*pname >= '0' && *pname <= '9') {
fd = 10 * fd + *pname++ - '0';
if (fd >= nfiles)
break;
}
if (*pname != '\0') {
error = ENOENT;
goto bad;
}
if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
error = EBADF;
goto bad;
}
error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
if (error)
goto bad;
VTOFDESC(fvp)->fd_fd = fd;
vn_lock(fvp, LK_SHARED | LK_RETRY, p);
*vpp = fvp;
return (0);
}
bad:;
vn_lock(dvp, LK_SHARED | LK_RETRY, p);
*vpp = NULL;
return (error);
}
int
fdesc_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
int error = 0;
switch (VTOFDESC(vp)->fd_type) {
case Fdesc:
/*
* XXX Kludge: set p->p_dupfd to contain the value of the
* the file descriptor being sought for duplication. The error
* return ensures that the vnode for this device will be
* released by vn_open. Open will detect this special error and
* take the actions in dupfdopen. Other callers of vn_open or
* VOP_OPEN will simply report the error.
*/
ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
error = ENODEV;
break;
case Fctty:
error = cttyopen(devctty, ap->a_mode, 0, ap->a_p);
break;
}
return (error);
}
static int
fdesc_attr(fd, vap, cred, p)
int fd;
struct vattr *vap;
struct ucred *cred;
struct proc *p;
{
struct filedesc *fdp = p->p_fd;
struct file *fp;
struct stat stb;
int error;
if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
return (EBADF);
switch (fp->f_type) {
case DTYPE_VNODE:
error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p);
if (error == 0 && vap->va_type == VDIR) {
/*
* directories can cause loops in the namespace,
* so turn off the 'x' bits to avoid trouble.
*/
vap->va_mode &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6));
}
break;
case DTYPE_SOCKET:
error = soo_stat((struct socket *)fp->f_data, &stb);
if (error == 0) {
vattr_null(vap);
vap->va_type = VSOCK;
vap->va_mode = stb.st_mode;
vap->va_nlink = stb.st_nlink;
vap->va_uid = stb.st_uid;
vap->va_gid = stb.st_gid;
vap->va_fsid = stb.st_dev;
vap->va_fileid = stb.st_ino;
vap->va_size = stb.st_size;
vap->va_blocksize = stb.st_blksize;
vap->va_atime = stb.st_atimespec;
vap->va_mtime = stb.st_mtimespec;
vap->va_ctime = stb.st_ctimespec;
vap->va_gen = stb.st_gen;
vap->va_flags = stb.st_flags;
vap->va_rdev = stb.st_rdev;
vap->va_bytes = stb.st_blocks * stb.st_blksize;
}
break;
default:
panic("fdesc attr");
break;
}
return (error);
}
int
fdesc_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
unsigned fd;
int error = 0;
switch (VTOFDESC(vp)->fd_type) {
case Froot:
case Fdevfd:
case Flink:
case Fctty:
bzero((caddr_t) vap, sizeof(*vap));
vattr_null(vap);
vap->va_fileid = VTOFDESC(vp)->fd_ix;
switch (VTOFDESC(vp)->fd_type) {
case Flink:
vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
vap->va_type = VLNK;
vap->va_nlink = 1;
vap->va_size = strlen(VTOFDESC(vp)->fd_link);
break;
case Fctty:
vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
vap->va_type = VFIFO;
vap->va_nlink = 1;
vap->va_size = 0;
break;
default:
vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
vap->va_type = VDIR;
vap->va_nlink = 2;
vap->va_size = DEV_BSIZE;
break;
}
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
vap->va_blocksize = DEV_BSIZE;
vap->va_atime.ts_sec = boottime.tv_sec;
vap->va_atime.ts_nsec = 0;
vap->va_mtime = vap->va_atime;
vap->va_ctime = vap->va_mtime;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_rdev = 0;
vap->va_bytes = 0;
break;
case Fdesc:
fd = VTOFDESC(vp)->fd_fd;
error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p);
break;
default:
panic("fdesc_getattr");
break;
}
if (error == 0)
vp->v_type = vap->va_type;
return (error);
}
int
fdesc_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct filedesc *fdp = ap->a_p->p_fd;
struct file *fp;
unsigned fd;
int error;
/*
* Can't mess with the root vnode
*/
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fdesc:
break;
case Fctty:
return (0);
default:
return (EACCES);
}
fd = VTOFDESC(ap->a_vp)->fd_fd;
if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) {
return (EBADF);
}
/*
* Can setattr the underlying vnode, but not sockets!
*/
switch (fp->f_type) {
case DTYPE_VNODE:
error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p);
break;
case DTYPE_SOCKET:
error = 0;
break;
default:
panic("fdesc setattr");
break;
}
return (error);
}
#define UIO_MX 16
static struct dirtmp {
u_long d_fileno;
u_short d_reclen;
u_short d_namlen;
char d_name[8];
} rootent[] = {
{ FD_DEVFD, UIO_MX, 2, "fd" },
{ FD_STDIN, UIO_MX, 5, "stdin" },
{ FD_STDOUT, UIO_MX, 6, "stdout" },
{ FD_STDERR, UIO_MX, 6, "stderr" },
{ FD_CTTY, UIO_MX, 3, "tty" },
{ 0 }
};
int
fdesc_readdir(ap)
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
u_long *a_cookies;
int a_ncookies;
} */ *ap;
{
struct uio *uio = ap->a_uio;
struct filedesc *fdp;
int i;
int error;
/*
* We don't allow exporting fdesc mounts, and currently local
* requests do not need cookies.
*/
if (ap->a_ncookies)
panic("fdesc_readdir: not hungry");
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
return (0);
case Fdesc:
return (ENOTDIR);
default:
break;
}
fdp = uio->uio_procp->p_fd;
if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
struct dirent d;
struct dirent *dp = &d;
struct dirtmp *dt;
i = uio->uio_offset / UIO_MX;
error = 0;
while (uio->uio_resid > 0) {
dt = &rootent[i];
if (dt->d_fileno == 0) {
/**eofflagp = 1;*/
break;
}
i++;
switch (dt->d_fileno) {
case FD_CTTY:
if (cttyvp(uio->uio_procp) == NULL)
continue;
break;
case FD_STDIN:
case FD_STDOUT:
case FD_STDERR:
if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles)
continue;
if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL)
continue;
break;
}
bzero((caddr_t) dp, UIO_MX);
dp->d_fileno = dt->d_fileno;
dp->d_namlen = dt->d_namlen;
dp->d_type = DT_UNKNOWN;
dp->d_reclen = dt->d_reclen;
bcopy(dt->d_name, dp->d_name, dp->d_namlen+1);
error = uiomove((caddr_t) dp, UIO_MX, uio);
if (error)
break;
}
uio->uio_offset = i * UIO_MX;
return (error);
}
i = uio->uio_offset / UIO_MX;
error = 0;
while (uio->uio_resid > 0) {
if (i >= fdp->fd_nfiles)
break;
if (fdp->fd_ofiles[i] != NULL) {
struct dirent d;
struct dirent *dp = &d;
bzero((caddr_t) dp, UIO_MX);
dp->d_namlen = sprintf(dp->d_name, "%d", i);
dp->d_reclen = UIO_MX;
dp->d_type = DT_UNKNOWN;
dp->d_fileno = i + FD_STDIN;
/*
* And ship to userland
*/
error = uiomove((caddr_t) dp, UIO_MX, uio);
if (error)
break;
}
i++;
}
uio->uio_offset = i * UIO_MX;
return (error);
}
int
fdesc_readlink(ap)
struct vop_readlink_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
int error;
if (vp->v_type != VLNK)
return (EPERM);
if (VTOFDESC(vp)->fd_type == Flink) {
char *ln = VTOFDESC(vp)->fd_link;
error = uiomove(ln, strlen(ln), ap->a_uio);
} else {
error = EOPNOTSUPP;
}
return (error);
}
int
fdesc_read(ap)
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
int error = EOPNOTSUPP;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
error = cttyread(devctty, ap->a_uio, ap->a_ioflag);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_write(ap)
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
int error = EOPNOTSUPP;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
error = cttywrite(devctty, ap->a_uio, ap->a_ioflag);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_ioctl(ap)
struct vop_ioctl_args /* {
struct vnode *a_vp;
int a_command;
caddr_t a_data;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
int error = EOPNOTSUPP;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
error = cttyioctl(devctty, ap->a_command, ap->a_data,
ap->a_fflag, ap->a_p);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_select(ap)
struct vop_select_args /* {
struct vnode *a_vp;
int a_which;
int a_fflags;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
int error = EOPNOTSUPP;
switch (VTOFDESC(ap->a_vp)->fd_type) {
case Fctty:
error = cttyselect(devctty, ap->a_fflags, ap->a_p);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
fdesc_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
/*
* Clear out the v_type field to avoid
* nasty things happening in vgone().
*/
VOP_UNLOCK(vp, 0, ap->a_p);
vp->v_type = VNON;
return (0);
}
int
fdesc_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct fdescnode *fd = VTOFDESC(vp);
LIST_REMOVE(fd, fd_hash);
FREE(vp->v_data, M_TEMP);
vp->v_data = 0;
return (0);
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
fdesc_pathconf(ap)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
int *a_retval;
} */ *ap;
{
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);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* Print out the contents of a /dev/fd vnode.
*/
/* ARGSUSED */
int
fdesc_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
printf("tag VT_NON, fdesc vnode\n");
return (0);
}
/*void*/
int
fdesc_vfree(ap)
struct vop_vfree_args /* {
struct vnode *a_pvp;
ino_t a_ino;
int a_mode;
} */ *ap;
{
return (0);
}
/*
* /dev/fd "should never get here" operation
*/
int
fdesc_badop()
{
panic("fdesc: bad op");
/* NOTREACHED */
}
#define fdesc_create ((int (*) __P((struct vop_create_args *)))eopnotsupp)
#define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp)
#define fdesc_close ((int (*) __P((struct vop_close_args *)))nullop)
#define fdesc_access ((int (*) __P((struct vop_access_args *)))nullop)
#define fdesc_mmap ((int (*) __P((struct vop_mmap_args *)))eopnotsupp)
#define fdesc_revoke vop_revoke
#define fdesc_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
#define fdesc_seek ((int (*) __P((struct vop_seek_args *)))nullop)
#define fdesc_remove ((int (*) __P((struct vop_remove_args *)))eopnotsupp)
#define fdesc_link ((int (*) __P((struct vop_link_args *)))eopnotsupp)
#define fdesc_rename ((int (*) __P((struct vop_rename_args *)))eopnotsupp)
#define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp)
#define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp)
#define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))eopnotsupp)
#define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
#define fdesc_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock)
#define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
#define fdesc_bmap ((int (*) __P((struct vop_bmap_args *)))fdesc_badop)
#define fdesc_strategy ((int (*) __P((struct vop_strategy_args *)))fdesc_badop)
#define fdesc_islocked \
((int (*) __P((struct vop_islocked_args *)))vop_noislocked)
#define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))eopnotsupp)
#define fdesc_blkatoff \
((int (*) __P((struct vop_blkatoff_args *)))eopnotsupp)
#define fdesc_valloc ((int(*) __P(( \
struct vnode *pvp, \
int mode, \
struct ucred *cred, \
struct vnode **vpp))) eopnotsupp)
#define fdesc_truncate \
((int (*) __P((struct vop_truncate_args *)))eopnotsupp)
#define fdesc_update ((int (*) __P((struct vop_update_args *)))eopnotsupp)
#define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp)
int (**fdesc_vnodeop_p)();
struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, fdesc_lookup }, /* lookup */
{ &vop_create_desc, fdesc_create }, /* create */
{ &vop_mknod_desc, fdesc_mknod }, /* mknod */
{ &vop_open_desc, fdesc_open }, /* open */
{ &vop_close_desc, fdesc_close }, /* close */
{ &vop_access_desc, fdesc_access }, /* access */
{ &vop_getattr_desc, fdesc_getattr }, /* getattr */
{ &vop_setattr_desc, fdesc_setattr }, /* setattr */
{ &vop_read_desc, fdesc_read }, /* read */
{ &vop_write_desc, fdesc_write }, /* write */
{ &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */
{ &vop_select_desc, fdesc_select }, /* select */
{ &vop_revoke_desc, fdesc_revoke }, /* revoke */
{ &vop_mmap_desc, fdesc_mmap }, /* mmap */
{ &vop_fsync_desc, fdesc_fsync }, /* fsync */
{ &vop_seek_desc, fdesc_seek }, /* seek */
{ &vop_remove_desc, fdesc_remove }, /* remove */
{ &vop_link_desc, fdesc_link }, /* link */
{ &vop_rename_desc, fdesc_rename }, /* rename */
{ &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */
{ &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */
{ &vop_symlink_desc, fdesc_symlink }, /* symlink */
{ &vop_readdir_desc, fdesc_readdir }, /* readdir */
{ &vop_readlink_desc, fdesc_readlink }, /* readlink */
{ &vop_abortop_desc, fdesc_abortop }, /* abortop */
{ &vop_inactive_desc, fdesc_inactive }, /* inactive */
{ &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */
{ &vop_lock_desc, fdesc_lock }, /* lock */
{ &vop_unlock_desc, fdesc_unlock }, /* unlock */
{ &vop_bmap_desc, fdesc_bmap }, /* bmap */
{ &vop_strategy_desc, fdesc_strategy }, /* strategy */
{ &vop_print_desc, fdesc_print }, /* print */
{ &vop_islocked_desc, fdesc_islocked }, /* islocked */
{ &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */
{ &vop_advlock_desc, fdesc_advlock }, /* advlock */
{ &vop_blkatoff_desc, fdesc_blkatoff }, /* blkatoff */
{ &vop_valloc_desc, fdesc_valloc }, /* valloc */
{ &vop_vfree_desc, fdesc_vfree }, /* vfree */
{ &vop_truncate_desc, fdesc_truncate }, /* truncate */
{ &vop_update_desc, fdesc_update }, /* update */
{ &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc fdesc_vnodeop_opv_desc =
{ &fdesc_vnodeop_p, fdesc_vnodeop_entries };

87
sys/miscfs/fifofs/fifo.h Normal file
View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)fifo.h 8.6 (Berkeley) 5/21/95
*/
#ifdef FIFO
/*
* Prototypes for fifo operations on vnodes.
*/
int fifo_badop(),
fifo_ebadf();
int fifo_lookup __P((struct vop_lookup_args *));
#define fifo_create ((int (*) __P((struct vop_create_args *)))fifo_badop)
#define fifo_mknod ((int (*) __P((struct vop_mknod_args *)))fifo_badop)
int fifo_open __P((struct vop_open_args *));
int fifo_close __P((struct vop_close_args *));
#define fifo_access ((int (*) __P((struct vop_access_args *)))fifo_ebadf)
#define fifo_getattr ((int (*) __P((struct vop_getattr_args *)))fifo_ebadf)
#define fifo_setattr ((int (*) __P((struct vop_setattr_args *)))fifo_ebadf)
int fifo_read __P((struct vop_read_args *));
int fifo_write __P((struct vop_write_args *));
#define fifo_lease_check ((int (*) __P((struct vop_lease_args *)))nullop)
int fifo_ioctl __P((struct vop_ioctl_args *));
int fifo_select __P((struct vop_select_args *));
#define fifo_revoke vop_revoke
#define fifo_mmap ((int (*) __P((struct vop_mmap_args *)))fifo_badop)
#define fifo_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
#define fifo_seek ((int (*) __P((struct vop_seek_args *)))fifo_badop)
#define fifo_remove ((int (*) __P((struct vop_remove_args *)))fifo_badop)
#define fifo_link ((int (*) __P((struct vop_link_args *)))fifo_badop)
#define fifo_rename ((int (*) __P((struct vop_rename_args *)))fifo_badop)
#define fifo_mkdir ((int (*) __P((struct vop_mkdir_args *)))fifo_badop)
#define fifo_rmdir ((int (*) __P((struct vop_rmdir_args *)))fifo_badop)
#define fifo_symlink ((int (*) __P((struct vop_symlink_args *)))fifo_badop)
#define fifo_readdir ((int (*) __P((struct vop_readdir_args *)))fifo_badop)
#define fifo_readlink ((int (*) __P((struct vop_readlink_args *)))fifo_badop)
#define fifo_abortop ((int (*) __P((struct vop_abortop_args *)))fifo_badop)
int fifo_inactive __P((struct vop_inactive_args *));
#define fifo_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
#define fifo_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock)
#define fifo_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
int fifo_bmap __P((struct vop_bmap_args *));
#define fifo_strategy ((int (*) __P((struct vop_strategy_args *)))fifo_badop)
int fifo_print __P((struct vop_print_args *));
#define fifo_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked)
int fifo_pathconf __P((struct vop_pathconf_args *));
int fifo_advlock __P((struct vop_advlock_args *));
#define fifo_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))fifo_badop)
#define fifo_valloc ((int (*) __P((struct vop_valloc_args *)))fifo_badop)
#define fifo_reallocblks \
((int (*) __P((struct vop_reallocblks_args *)))fifo_badop)
#define fifo_vfree ((int (*) __P((struct vop_vfree_args *)))fifo_badop)
#define fifo_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
#define fifo_update ((int (*) __P((struct vop_update_args *)))nullop)
#define fifo_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
#endif /* FIFO */

View file

@ -0,0 +1,515 @@
/*
* Copyright (c) 1990, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)fifo_vnops.c 8.10 (Berkeley) 5/27/95
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <miscfs/fifofs/fifo.h>
/*
* This structure is associated with the FIFO vnode and stores
* the state associated with the FIFO.
*/
struct fifoinfo {
struct socket *fi_readsock;
struct socket *fi_writesock;
long fi_readers;
long fi_writers;
};
int (**fifo_vnodeop_p)();
struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, fifo_lookup }, /* lookup */
{ &vop_create_desc, fifo_create }, /* create */
{ &vop_mknod_desc, fifo_mknod }, /* mknod */
{ &vop_open_desc, fifo_open }, /* open */
{ &vop_close_desc, fifo_close }, /* close */
{ &vop_access_desc, fifo_access }, /* access */
{ &vop_getattr_desc, fifo_getattr }, /* getattr */
{ &vop_setattr_desc, fifo_setattr }, /* setattr */
{ &vop_read_desc, fifo_read }, /* read */
{ &vop_write_desc, fifo_write }, /* write */
{ &vop_lease_desc, fifo_lease_check }, /* lease */
{ &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
{ &vop_select_desc, fifo_select }, /* select */
{ &vop_revoke_desc, fifo_revoke }, /* revoke */
{ &vop_mmap_desc, fifo_mmap }, /* mmap */
{ &vop_fsync_desc, fifo_fsync }, /* fsync */
{ &vop_seek_desc, fifo_seek }, /* seek */
{ &vop_remove_desc, fifo_remove }, /* remove */
{ &vop_link_desc, fifo_link }, /* link */
{ &vop_rename_desc, fifo_rename }, /* rename */
{ &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
{ &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
{ &vop_symlink_desc, fifo_symlink }, /* symlink */
{ &vop_readdir_desc, fifo_readdir }, /* readdir */
{ &vop_readlink_desc, fifo_readlink }, /* readlink */
{ &vop_abortop_desc, fifo_abortop }, /* abortop */
{ &vop_inactive_desc, fifo_inactive }, /* inactive */
{ &vop_reclaim_desc, fifo_reclaim }, /* reclaim */
{ &vop_lock_desc, fifo_lock }, /* lock */
{ &vop_unlock_desc, fifo_unlock }, /* unlock */
{ &vop_bmap_desc, fifo_bmap }, /* bmap */
{ &vop_strategy_desc, fifo_strategy }, /* strategy */
{ &vop_print_desc, fifo_print }, /* print */
{ &vop_islocked_desc, fifo_islocked }, /* islocked */
{ &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
{ &vop_advlock_desc, fifo_advlock }, /* advlock */
{ &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
{ &vop_valloc_desc, fifo_valloc }, /* valloc */
{ &vop_vfree_desc, fifo_vfree }, /* vfree */
{ &vop_truncate_desc, fifo_truncate }, /* truncate */
{ &vop_update_desc, fifo_update }, /* update */
{ &vop_bwrite_desc, fifo_bwrite }, /* bwrite */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc fifo_vnodeop_opv_desc =
{ &fifo_vnodeop_p, fifo_vnodeop_entries };
/*
* Trivial lookup routine that always fails.
*/
/* ARGSUSED */
fifo_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
*ap->a_vpp = NULL;
return (ENOTDIR);
}
/*
* Open called to set up a new instance of a fifo or
* to find an active instance of a fifo.
*/
/* ARGSUSED */
fifo_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct fifoinfo *fip;
struct proc *p = ap->a_p;
struct socket *rso, *wso;
int error;
static char openstr[] = "fifo";
if ((fip = vp->v_fifoinfo) == NULL) {
MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK);
vp->v_fifoinfo = fip;
if (error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0)) {
free(fip, M_VNODE);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_readsock = rso;
if (error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0)) {
(void)soclose(rso);
free(fip, M_VNODE);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_writesock = wso;
if (error = unp_connect2(wso, rso)) {
(void)soclose(wso);
(void)soclose(rso);
free(fip, M_VNODE);
vp->v_fifoinfo = NULL;
return (error);
}
fip->fi_readers = fip->fi_writers = 0;
wso->so_state |= SS_CANTRCVMORE;
rso->so_state |= SS_CANTSENDMORE;
}
if (ap->a_mode & FREAD) {
fip->fi_readers++;
if (fip->fi_readers == 1) {
fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
if (fip->fi_writers > 0)
wakeup((caddr_t)&fip->fi_writers);
}
}
if (ap->a_mode & FWRITE) {
fip->fi_writers++;
if (fip->fi_writers == 1) {
fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
if (fip->fi_readers > 0)
wakeup((caddr_t)&fip->fi_readers);
}
}
if ((ap->a_mode & FREAD) && (ap->a_mode & O_NONBLOCK) == 0) {
while (fip->fi_writers == 0) {
VOP_UNLOCK(vp, 0, p);
error = tsleep((caddr_t)&fip->fi_readers,
PCATCH | PSOCK, openstr, 0);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (error)
goto bad;
}
}
if (ap->a_mode & FWRITE) {
if (ap->a_mode & O_NONBLOCK) {
if (fip->fi_readers == 0) {
error = ENXIO;
goto bad;
}
} else {
while (fip->fi_readers == 0) {
VOP_UNLOCK(vp, 0, p);
error = tsleep((caddr_t)&fip->fi_writers,
PCATCH | PSOCK, openstr, 0);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (error)
goto bad;
}
}
}
return (0);
bad:
if (error)
VOP_CLOSE(vp, ap->a_mode, ap->a_cred, p);
return (error);
}
/*
* Vnode op for read
*/
/* ARGSUSED */
fifo_read(ap)
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
struct uio *uio = ap->a_uio;
struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
struct proc *p = uio->uio_procp;
int error, startresid;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("fifo_read mode");
#endif
if (uio->uio_resid == 0)
return (0);
if (ap->a_ioflag & IO_NDELAY)
rso->so_state |= SS_NBIO;
startresid = uio->uio_resid;
VOP_UNLOCK(ap->a_vp, 0, p);
error = soreceive(rso, (struct mbuf **)0, uio, (struct mbuf **)0,
(struct mbuf **)0, (int *)0);
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
/*
* Clear EOF indication after first such return.
*/
if (uio->uio_resid == startresid)
rso->so_state &= ~SS_CANTRCVMORE;
if (ap->a_ioflag & IO_NDELAY)
rso->so_state &= ~SS_NBIO;
return (error);
}
/*
* Vnode op for write
*/
/* ARGSUSED */
fifo_write(ap)
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
struct proc *p = ap->a_uio->uio_procp;
int error;
#ifdef DIAGNOSTIC
if (ap->a_uio->uio_rw != UIO_WRITE)
panic("fifo_write mode");
#endif
if (ap->a_ioflag & IO_NDELAY)
wso->so_state |= SS_NBIO;
VOP_UNLOCK(ap->a_vp, 0, p);
error = sosend(wso, (struct mbuf *)0, ap->a_uio, 0, (struct mbuf *)0, 0);
vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (ap->a_ioflag & IO_NDELAY)
wso->so_state &= ~SS_NBIO;
return (error);
}
/*
* Device ioctl operation.
*/
/* ARGSUSED */
fifo_ioctl(ap)
struct vop_ioctl_args /* {
struct vnode *a_vp;
int a_command;
caddr_t a_data;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct file filetmp;
int error;
if (ap->a_command == FIONBIO)
return (0);
if (ap->a_fflag & FREAD) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
if (error)
return (error);
}
if (ap->a_fflag & FWRITE) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
if (error)
return (error);
}
return (0);
}
/* ARGSUSED */
fifo_select(ap)
struct vop_select_args /* {
struct vnode *a_vp;
int a_which;
int a_fflags;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct file filetmp;
int ready;
if (ap->a_fflags & FREAD) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
ready = soo_select(&filetmp, ap->a_which, ap->a_p);
if (ready)
return (ready);
}
if (ap->a_fflags & FWRITE) {
filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
ready = soo_select(&filetmp, ap->a_which, ap->a_p);
if (ready)
return (ready);
}
return (0);
}
int
fifo_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return (0);
}
/*
* This is a noop, simply returning what one has been given.
*/
fifo_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct vnode **a_vpp;
daddr_t *a_bnp;
int *a_runp;
} */ *ap;
{
if (ap->a_vpp != NULL)
*ap->a_vpp = ap->a_vp;
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
return (0);
}
/*
* Device close routine
*/
/* ARGSUSED */
fifo_close(ap)
struct vop_close_args /* {
struct vnode *a_vp;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct fifoinfo *fip = vp->v_fifoinfo;
int error1, error2;
if (ap->a_fflag & FREAD) {
fip->fi_readers--;
if (fip->fi_readers == 0)
socantsendmore(fip->fi_writesock);
}
if (ap->a_fflag & FWRITE) {
fip->fi_writers--;
if (fip->fi_writers == 0)
socantrcvmore(fip->fi_readsock);
}
if (vp->v_usecount > 1)
return (0);
error1 = soclose(fip->fi_readsock);
error2 = soclose(fip->fi_writesock);
FREE(fip, M_VNODE);
vp->v_fifoinfo = NULL;
if (error1)
return (error1);
return (error2);
}
/*
* Print out the contents of a fifo vnode.
*/
fifo_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
printf("tag VT_NON");
fifo_printinfo(ap->a_vp);
printf("\n");
}
/*
* Print out internal contents of a fifo vnode.
*/
fifo_printinfo(vp)
struct vnode *vp;
{
register struct fifoinfo *fip = vp->v_fifoinfo;
printf(", fifo with %d readers and %d writers",
fip->fi_readers, fip->fi_writers);
}
/*
* Return POSIX pathconf information applicable to fifo's.
*/
fifo_pathconf(ap)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
int *a_retval;
} */ *ap;
{
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
return (0);
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
return (0);
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
return (0);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* Fifo failed operation
*/
fifo_ebadf()
{
return (EBADF);
}
/*
* Fifo advisory byte-level locks.
*/
/* ARGSUSED */
fifo_advlock(ap)
struct vop_advlock_args /* {
struct vnode *a_vp;
caddr_t a_id;
int a_op;
struct flock *a_fl;
int a_flags;
} */ *ap;
{
return (EOPNOTSUPP);
}
/*
* Fifo bad operation
*/
fifo_badop()
{
panic("fifo_badop called");
/* NOTREACHED */
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)kernfs.h 8.6 (Berkeley) 3/29/95
*/
#define _PATH_KERNFS "/kern" /* Default mountpoint */
#ifdef KERNEL
struct kernfs_mount {
struct vnode *kf_root; /* Root node */
};
struct kernfs_node {
struct kern_target *kf_kt;
};
#define VFSTOKERNFS(mp) ((struct kernfs_mount *)((mp)->mnt_data))
#define VTOKERN(vp) ((struct kernfs_node *)(vp)->v_data)
#define kernfs_fhtovp ((int (*) __P((struct mount *, struct fid *, \
struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
#define kernfs_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
struct proc *)))eopnotsupp)
#define kernfs_sync ((int (*) __P((struct mount *, int, struct ucred *, \
struct proc *)))nullop)
#define kernfs_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
#define kernfs_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
eopnotsupp)
#define kernfs_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
extern int (**kernfs_vnodeop_p)();
extern struct vfsops kernfs_vfsops;
extern dev_t rrootdev;
#endif /* KERNEL */

View file

@ -0,0 +1,257 @@
/*
* 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. 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.
*
* @(#)kernfs_vfsops.c 8.10 (Berkeley) 5/14/95
*/
/*
* Kernel params Filesystem
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <miscfs/specfs/specdev.h>
#include <miscfs/kernfs/kernfs.h>
dev_t rrootdev = NODEV;
kernfs_init(vfsp)
struct vfsconf *vfsp;
{
return (0);
}
void
kernfs_get_rrootdev()
{
static int tried = 0;
int cmaj;
if (tried) {
/* Already did it once. */
return;
}
tried = 1;
if (rootdev == NODEV)
return;
for (cmaj = 0; cmaj < nchrdev; cmaj++) {
rrootdev = makedev(cmaj, minor(rootdev));
if (chrtoblk(rrootdev) == rootdev)
return;
}
rrootdev = NODEV;
printf("kernfs_get_rrootdev: no raw root device\n");
}
/*
* Mount the Kernel params filesystem
*/
kernfs_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
int error = 0;
u_int size;
struct kernfs_mount *fmp;
struct vnode *rvp;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_mount(mp = %x)\n", mp);
#endif
/*
* Update is a no-op
*/
if (mp->mnt_flag & MNT_UPDATE)
return (EOPNOTSUPP);
error = getnewvnode(VT_KERNFS, mp, kernfs_vnodeop_p, &rvp); /* XXX */
if (error)
return (error);
MALLOC(fmp, struct kernfs_mount *, sizeof(struct kernfs_mount),
M_UFSMNT, M_WAITOK); /* XXX */
rvp->v_type = VDIR;
rvp->v_flag |= VROOT;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_mount: root vp = %x\n", rvp);
#endif
fmp->kf_root = rvp;
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = (qaddr_t) fmp;
vfs_getnewfsid(mp);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
bcopy("kernfs", mp->mnt_stat.f_mntfromname, sizeof("kernfs"));
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_mount: at %s\n", mp->mnt_stat.f_mntonname);
#endif
kernfs_get_rrootdev();
return (0);
}
kernfs_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
}
kernfs_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
int error;
int flags = 0;
struct vnode *rootvp = VFSTOKERNFS(mp)->kf_root;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_unmount(mp = %x)\n", mp);
#endif
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
/*
* Clear out buffer cache. I don't think we
* ever get anything cached at this level at the
* moment, but who knows...
*/
if (rootvp->v_usecount > 1)
return (EBUSY);
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_unmount: calling vflush\n");
#endif
if (error = vflush(mp, rootvp, flags))
return (error);
#ifdef KERNFS_DIAGNOSTIC
vprint("kernfs root", rootvp);
#endif
/*
* Release reference on underlying root vnode
*/
vrele(rootvp);
/*
* And blow it away for future re-use
*/
vgone(rootvp);
/*
* Finally, throw away the kernfs_mount structure
*/
free(mp->mnt_data, M_UFSMNT); /* XXX */
mp->mnt_data = 0;
return 0;
}
kernfs_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct vnode *vp;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_root(mp = %x)\n", mp);
#endif
/*
* Return locked reference to root.
*/
vp = VFSTOKERNFS(mp)->kf_root;
VREF(vp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
*vpp = vp;
return (0);
}
kernfs_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_statfs(mp = %x)\n", mp);
#endif
sbp->f_flags = 0;
sbp->f_bsize = 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_files = 0;
sbp->f_ffree = 0;
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
struct vfsops kernfs_vfsops = {
kernfs_mount,
kernfs_start,
kernfs_unmount,
kernfs_root,
kernfs_quotactl,
kernfs_statfs,
kernfs_sync,
kernfs_vget,
kernfs_fhtovp,
kernfs_vptofh,
kernfs_init,
kernfs_sysctl,
};

View file

@ -0,0 +1,760 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)kernfs_vnops.c 8.15 (Berkeley) 5/21/95
*/
/*
* Kernel parameter filesystem (/kern)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/vmmeter.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/buf.h>
#include <sys/dirent.h>
#include <miscfs/kernfs/kernfs.h>
#define KSTRING 256 /* Largest I/O available via this filesystem */
#define UIO_MX 32
#define READ_MODE (S_IRUSR|S_IRGRP|S_IROTH)
#define WRITE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
struct kern_target {
u_char kt_type;
u_char kt_namlen;
char *kt_name;
void *kt_data;
#define KTT_NULL 1
#define KTT_TIME 5
#define KTT_INT 17
#define KTT_STRING 31
#define KTT_HOSTNAME 47
#define KTT_AVENRUN 53
#define KTT_DEVICE 71
u_char kt_tag;
u_char kt_vtype;
mode_t kt_mode;
} kern_targets[] = {
/* NOTE: The name must be less than UIO_MX-16 chars in length */
#define N(s) sizeof(s)-1, s
/* name data tag type ro/rw */
{ DT_DIR, N("."), 0, KTT_NULL, VDIR, DIR_MODE },
{ DT_DIR, N(".."), 0, KTT_NULL, VDIR, DIR_MODE },
{ DT_REG, N("boottime"), &boottime.tv_sec, KTT_INT, VREG, READ_MODE },
{ DT_REG, N("copyright"), copyright, KTT_STRING, VREG, READ_MODE },
{ DT_REG, N("hostname"), 0, KTT_HOSTNAME, VREG, WRITE_MODE },
{ DT_REG, N("hz"), &hz, KTT_INT, VREG, READ_MODE },
{ DT_REG, N("loadavg"), 0, KTT_AVENRUN, VREG, READ_MODE },
{ DT_REG, N("pagesize"), &cnt.v_page_size, KTT_INT, VREG, READ_MODE },
{ DT_REG, N("physmem"), &physmem, KTT_INT, VREG, READ_MODE },
#if 0
{ DT_DIR, N("root"), 0, KTT_NULL, VDIR, DIR_MODE },
#endif
{ DT_BLK, N("rootdev"), &rootdev, KTT_DEVICE, VBLK, READ_MODE },
{ DT_CHR, N("rrootdev"), &rrootdev, KTT_DEVICE, VCHR, READ_MODE },
{ DT_REG, N("time"), 0, KTT_TIME, VREG, READ_MODE },
{ DT_REG, N("version"), version, KTT_STRING, VREG, READ_MODE },
#undef N
};
static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]);
static int
kernfs_xread(kt, buf, len, lenp)
struct kern_target *kt;
char *buf;
int len;
int *lenp;
{
switch (kt->kt_tag) {
case KTT_TIME: {
struct timeval tv;
microtime(&tv);
sprintf(buf, "%d %d\n", tv.tv_sec, tv.tv_usec);
break;
}
case KTT_INT: {
int *ip = kt->kt_data;
sprintf(buf, "%d\n", *ip);
break;
}
case KTT_STRING: {
char *cp = kt->kt_data;
int xlen = strlen(cp) + 1;
if (xlen >= len)
return (EINVAL);
bcopy(cp, buf, xlen);
break;
}
case KTT_HOSTNAME: {
char *cp = hostname;
int xlen = hostnamelen;
if (xlen >= (len-2))
return (EINVAL);
bcopy(cp, buf, xlen);
buf[xlen] = '\n';
buf[xlen+1] = '\0';
break;
}
case KTT_AVENRUN:
sprintf(buf, "%ld %ld %ld %ld\n",
averunnable.ldavg[0], averunnable.ldavg[1],
averunnable.ldavg[2], averunnable.fscale);
break;
default:
return (EIO);
}
*lenp = strlen(buf);
return (0);
}
static int
kernfs_xwrite(kt, buf, len)
struct kern_target *kt;
char *buf;
int len;
{
switch (kt->kt_tag) {
case KTT_HOSTNAME:
if (buf[len-1] == '\n')
--len;
bcopy(buf, hostname, len);
hostname[len] = '\0';
hostnamelen = len;
return (0);
default:
return (EIO);
}
}
/*
* vp is the current namei directory
* ndp is the name to locate in that directory...
*/
kernfs_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
struct componentname *cnp = ap->a_cnp;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
char *pname = cnp->cn_nameptr;
struct proc *p = cnp->cn_proc;
struct kern_target *kt;
struct vnode *fvp;
int error, i;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_lookup(%x)\n", ap);
printf("kernfs_lookup(dp = %x, vpp = %x, cnp = %x)\n", dvp, vpp, ap->a_cnp);
printf("kernfs_lookup(%s)\n", pname);
#endif
*vpp = NULLVP;
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
return (EROFS);
VOP_UNLOCK(dvp, 0, p);
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
VREF(dvp);
vn_lock(dvp, LK_SHARED | LK_RETRY, p);
return (0);
}
#if 0
if (cnp->cn_namelen == 4 && bcmp(pname, "root", 4) == 0) {
*vpp = rootdir;
VREF(rootdir);
vn_lock(rootdir, LK_SHARED | LK_RETRY, p)
return (0);
}
#endif
for (kt = kern_targets, i = 0; i < nkern_targets; kt++, i++) {
if (cnp->cn_namelen == kt->kt_namlen &&
bcmp(kt->kt_name, pname, cnp->cn_namelen) == 0)
goto found;
}
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_lookup: i = %d, failed", i);
#endif
vn_lock(dvp, LK_SHARED | LK_RETRY, p);
return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS);
found:
if (kt->kt_tag == KTT_DEVICE) {
dev_t *dp = kt->kt_data;
loop:
if (*dp == NODEV || !vfinddev(*dp, kt->kt_vtype, &fvp)) {
vn_lock(dvp, LK_SHARED | LK_RETRY, p);
return (ENOENT);
}
*vpp = fvp;
if (vget(fvp, LK_EXCLUSIVE, p))
goto loop;
return (0);
}
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_lookup: allocate new vnode\n");
#endif
if (error = getnewvnode(VT_KERNFS, dvp->v_mount, kernfs_vnodeop_p,
&fvp)) {
vn_lock(dvp, LK_SHARED | LK_RETRY, p);
return (error);
}
MALLOC(fvp->v_data, void *, sizeof(struct kernfs_node), M_TEMP,
M_WAITOK);
VTOKERN(fvp)->kf_kt = kt;
fvp->v_type = kt->kt_vtype;
vn_lock(fvp, LK_SHARED | LK_RETRY, p);
*vpp = fvp;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_lookup: newvp = %x\n", fvp);
#endif
return (0);
}
kernfs_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
/* Only need to check access permissions. */
return (0);
}
static int
kernfs_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct ucred *cred = ap->a_cred;
mode_t amode = ap->a_mode;
mode_t fmode =
(vp->v_flag & VROOT) ? DIR_MODE : VTOKERN(vp)->kf_kt->kt_mode;
mode_t mask = 0;
register gid_t *gp;
int i;
/* Some files are simply not modifiable. */
if ((amode & VWRITE) && (fmode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0)
return (EPERM);
/* Root can do anything else. */
if (cred->cr_uid == 0)
return (0);
/* Check for group 0 (wheel) permissions. */
for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
if (*gp == 0) {
if (amode & VEXEC)
mask |= S_IXGRP;
if (amode & VREAD)
mask |= S_IRGRP;
if (amode & VWRITE)
mask |= S_IWGRP;
return ((fmode & mask) == mask ? 0 : EACCES);
}
/* Otherwise, check everyone else. */
if (amode & VEXEC)
mask |= S_IXOTH;
if (amode & VREAD)
mask |= S_IROTH;
if (amode & VWRITE)
mask |= S_IWOTH;
return ((fmode & mask) == mask ? 0 : EACCES);
}
kernfs_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
struct timeval tv;
int error = 0;
char strbuf[KSTRING];
bzero((caddr_t) vap, sizeof(*vap));
vattr_null(vap);
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
vap->va_size = 0;
vap->va_blocksize = DEV_BSIZE;
microtime(&tv);
TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime);
vap->va_mtime = vap->va_atime;
vap->va_ctime = vap->va_ctime;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_rdev = 0;
vap->va_bytes = 0;
if (vp->v_flag & VROOT) {
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_getattr: stat rootdir\n");
#endif
vap->va_type = VDIR;
vap->va_mode = DIR_MODE;
vap->va_nlink = 2;
vap->va_fileid = 2;
vap->va_size = DEV_BSIZE;
} else {
struct kern_target *kt = VTOKERN(vp)->kf_kt;
int nbytes;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_getattr: stat target %s\n", kt->kt_name);
#endif
vap->va_type = kt->kt_vtype;
vap->va_mode = kt->kt_mode;
vap->va_nlink = 1;
vap->va_fileid = 1 + (kt - kern_targets) / sizeof(*kt);
error = kernfs_xread(kt, strbuf, sizeof(strbuf), &nbytes);
vap->va_size = nbytes;
}
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_getattr: return error %d\n", error);
#endif
return (error);
}
kernfs_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
/*
* Silently ignore attribute changes.
* This allows for open with truncate to have no
* effect until some data is written. I want to
* do it this way because all writes are atomic.
*/
return (0);
}
static int
kernfs_read(ap)
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct kern_target *kt;
char strbuf[KSTRING];
int off = uio->uio_offset;
int error, len;
char *cp;
if (vp->v_type == VDIR)
return (EOPNOTSUPP);
kt = VTOKERN(vp)->kf_kt;
#ifdef KERNFS_DIAGNOSTIC
printf("kern_read %s\n", kt->kt_name);
#endif
len = 0;
if (error = kernfs_xread(kt, strbuf, sizeof(strbuf), &len))
return (error);
if (len <= off)
return (0);
return (uiomove(&strbuf[off], len - off, uio));
}
static int
kernfs_write(ap)
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct kern_target *kt;
int error, xlen;
char strbuf[KSTRING];
if (vp->v_type == VDIR)
return (EOPNOTSUPP);
kt = VTOKERN(vp)->kf_kt;
if (uio->uio_offset != 0)
return (EINVAL);
xlen = min(uio->uio_resid, KSTRING-1);
if (error = uiomove(strbuf, xlen, uio))
return (error);
if (uio->uio_resid != 0)
return (EIO);
strbuf[xlen] = '\0';
xlen = strlen(strbuf);
return (kernfs_xwrite(kt, strbuf, xlen));
}
kernfs_readdir(ap)
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
u_long *a_cookies;
int a_ncookies;
} */ *ap;
{
int error, i;
struct uio *uio = ap->a_uio;
struct kern_target *kt;
struct dirent d;
if (ap->a_vp->v_type != VDIR)
return (ENOTDIR);
/*
* We don't allow exporting kernfs mounts, and currently local
* requests do not need cookies.
*/
if (ap->a_ncookies != NULL)
panic("kernfs_readdir: not hungry");
i = uio->uio_offset / UIO_MX;
error = 0;
for (kt = &kern_targets[i];
uio->uio_resid >= UIO_MX && i < nkern_targets; kt++, i++) {
struct dirent *dp = &d;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_readdir: i = %d\n", i);
#endif
if (kt->kt_tag == KTT_DEVICE) {
dev_t *dp = kt->kt_data;
struct vnode *fvp;
if (*dp == NODEV || !vfinddev(*dp, kt->kt_vtype, &fvp))
continue;
}
bzero((caddr_t)dp, UIO_MX);
dp->d_namlen = kt->kt_namlen;
bcopy(kt->kt_name, dp->d_name, kt->kt_namlen+1);
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_readdir: name = %s, len = %d\n",
dp->d_name, dp->d_namlen);
#endif
/*
* Fill in the remaining fields
*/
dp->d_reclen = UIO_MX;
dp->d_fileno = i + 3;
dp->d_type = kt->kt_type;
/*
* And ship to userland
*/
if (error = uiomove((caddr_t)dp, UIO_MX, uio))
break;
}
uio->uio_offset = i * UIO_MX;
return (error);
}
kernfs_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_inactive(%x)\n", vp);
#endif
/*
* Clear out the v_type field to avoid
* nasty things happening in vgone().
*/
VOP_UNLOCK(vp, 0, ap->a_p);
vp->v_type = VNON;
return (0);
}
kernfs_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
#ifdef KERNFS_DIAGNOSTIC
printf("kernfs_reclaim(%x)\n", vp);
#endif
if (vp->v_data) {
FREE(vp->v_data, M_TEMP);
vp->v_data = 0;
}
return (0);
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
kernfs_pathconf(ap)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
int *a_retval;
} */ *ap;
{
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);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* Print out the contents of a /dev/fd vnode.
*/
/* ARGSUSED */
kernfs_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
printf("tag VT_KERNFS, kernfs vnode\n");
return (0);
}
/*void*/
kernfs_vfree(ap)
struct vop_vfree_args /* {
struct vnode *a_pvp;
ino_t a_ino;
int a_mode;
} */ *ap;
{
return (0);
}
/*
* /dev/fd "should never get here" operation
*/
kernfs_badop()
{
panic("kernfs: bad op");
/* NOTREACHED */
}
/*
* kernfs vnode null operation
*/
kernfs_nullop()
{
return (0);
}
#define kernfs_create ((int (*) __P((struct vop_create_args *)))eopnotsupp)
#define kernfs_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp)
#define kernfs_close ((int (*) __P((struct vop_close_args *)))nullop)
#define kernfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))eopnotsupp)
#define kernfs_select ((int (*) __P((struct vop_select_args *)))eopnotsupp)
#define kernfs_revoke vop_revoke
#define kernfs_mmap ((int (*) __P((struct vop_mmap_args *)))eopnotsupp)
#define kernfs_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
#define kernfs_seek ((int (*) __P((struct vop_seek_args *)))nullop)
#define kernfs_remove ((int (*) __P((struct vop_remove_args *)))eopnotsupp)
#define kernfs_link ((int (*) __P((struct vop_link_args *)))eopnotsupp)
#define kernfs_rename ((int (*) __P((struct vop_rename_args *)))eopnotsupp)
#define kernfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp)
#define kernfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp)
#define kernfs_symlink ((int (*) __P((struct vop_symlink_args *)))eopnotsupp)
#define kernfs_readlink ((int (*) __P((struct vop_readlink_args *)))eopnotsupp)
#define kernfs_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
#define kernfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock)
#define kernfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
#define kernfs_bmap ((int (*) __P((struct vop_bmap_args *)))kernfs_badop)
#define kernfs_strategy \
((int (*) __P((struct vop_strategy_args *)))kernfs_badop)
#define kernfs_islocked \
((int (*) __P((struct vop_islocked_args *)))vop_noislocked)
#define kernfs_advlock ((int (*) __P((struct vop_advlock_args *)))eopnotsupp)
#define kernfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))eopnotsupp)
#define kernfs_valloc ((int(*) __P(( \
struct vnode *pvp, \
int mode, \
struct ucred *cred, \
struct vnode **vpp))) eopnotsupp)
#define kernfs_truncate ((int (*) __P((struct vop_truncate_args *)))eopnotsupp)
#define kernfs_update ((int (*) __P((struct vop_update_args *)))eopnotsupp)
#define kernfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp)
int (**kernfs_vnodeop_p)();
struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, kernfs_lookup }, /* lookup */
{ &vop_create_desc, kernfs_create }, /* create */
{ &vop_mknod_desc, kernfs_mknod }, /* mknod */
{ &vop_open_desc, kernfs_open }, /* open */
{ &vop_close_desc, kernfs_close }, /* close */
{ &vop_access_desc, kernfs_access }, /* access */
{ &vop_getattr_desc, kernfs_getattr }, /* getattr */
{ &vop_setattr_desc, kernfs_setattr }, /* setattr */
{ &vop_read_desc, kernfs_read }, /* read */
{ &vop_write_desc, kernfs_write }, /* write */
{ &vop_ioctl_desc, kernfs_ioctl }, /* ioctl */
{ &vop_select_desc, kernfs_select }, /* select */
{ &vop_revoke_desc, kernfs_revoke }, /* revoke */
{ &vop_mmap_desc, kernfs_mmap }, /* mmap */
{ &vop_fsync_desc, kernfs_fsync }, /* fsync */
{ &vop_seek_desc, kernfs_seek }, /* seek */
{ &vop_remove_desc, kernfs_remove }, /* remove */
{ &vop_link_desc, kernfs_link }, /* link */
{ &vop_rename_desc, kernfs_rename }, /* rename */
{ &vop_mkdir_desc, kernfs_mkdir }, /* mkdir */
{ &vop_rmdir_desc, kernfs_rmdir }, /* rmdir */
{ &vop_symlink_desc, kernfs_symlink }, /* symlink */
{ &vop_readdir_desc, kernfs_readdir }, /* readdir */
{ &vop_readlink_desc, kernfs_readlink },/* readlink */
{ &vop_abortop_desc, kernfs_abortop }, /* abortop */
{ &vop_inactive_desc, kernfs_inactive },/* inactive */
{ &vop_reclaim_desc, kernfs_reclaim }, /* reclaim */
{ &vop_lock_desc, kernfs_lock }, /* lock */
{ &vop_unlock_desc, kernfs_unlock }, /* unlock */
{ &vop_bmap_desc, kernfs_bmap }, /* bmap */
{ &vop_strategy_desc, kernfs_strategy },/* strategy */
{ &vop_print_desc, kernfs_print }, /* print */
{ &vop_islocked_desc, kernfs_islocked },/* islocked */
{ &vop_pathconf_desc, kernfs_pathconf },/* pathconf */
{ &vop_advlock_desc, kernfs_advlock }, /* advlock */
{ &vop_blkatoff_desc, kernfs_blkatoff },/* blkatoff */
{ &vop_valloc_desc, kernfs_valloc }, /* valloc */
{ &vop_vfree_desc, kernfs_vfree }, /* vfree */
{ &vop_truncate_desc, kernfs_truncate },/* truncate */
{ &vop_update_desc, kernfs_update }, /* update */
{ &vop_bwrite_desc, kernfs_bwrite }, /* bwrite */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc kernfs_vnodeop_opv_desc =
{ &kernfs_vnodeop_p, kernfs_vnodeop_entries };

74
sys/miscfs/nullfs/null.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)null.h 8.3 (Berkeley) 8/20/94
*
* $Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp jsp $
*/
struct null_args {
char *target; /* Target of loopback */
};
struct null_mount {
struct mount *nullm_vfs;
struct vnode *nullm_rootvp; /* Reference to root null_node */
};
#ifdef KERNEL
/*
* A cache of vnode references
*/
struct null_node {
LIST_ENTRY(null_node) null_hash; /* Hash list */
struct vnode *null_lowervp; /* VREFed once */
struct vnode *null_vnode; /* Back pointer */
};
extern int null_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
#define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data))
#define VTONULL(vp) ((struct null_node *)(vp)->v_data)
#define NULLTOV(xp) ((xp)->null_vnode)
#ifdef NULLFS_DIAGNOSTIC
extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno));
#define NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__)
#else
#define NULLVPTOLOWERVP(vp) (VTONULL(vp)->null_lowervp)
#endif
extern int (**null_vnodeop_p)();
extern struct vfsops null_vfsops;
#endif /* KERNEL */

View file

@ -0,0 +1,275 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)null_subr.c 8.7 (Berkeley) 5/14/95
*
* $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <miscfs/nullfs/null.h>
#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
#define NNULLNODECACHE 16
/*
* Null layer cache:
* Each cache entry holds a reference to the lower vnode
* along with a pointer to the alias vnode. When an
* entry is added the lower vnode is VREF'd. When the
* alias is removed the lower vnode is vrele'd.
*/
#define NULL_NHASH(vp) \
(&null_node_hashtbl[(((u_long)vp)>>LOG2_SIZEVNODE) & null_node_hash])
LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
u_long null_node_hash;
/*
* Initialise cache headers
*/
nullfs_init()
{
#ifdef NULLFS_DIAGNOSTIC
printf("nullfs_init\n"); /* printed during system boot */
#endif
null_node_hashtbl = hashinit(NNULLNODECACHE, M_CACHE, &null_node_hash);
}
/*
* Return a VREF'ed alias for lower vnode if already exists, else 0.
*/
static struct vnode *
null_node_find(mp, lowervp)
struct mount *mp;
struct vnode *lowervp;
{
struct proc *p = curproc; /* XXX */
struct null_node_hashhead *hd;
struct null_node *a;
struct vnode *vp;
/*
* Find hash base, and then search the (two-way) linked
* list looking for a null_node structure which is referencing
* the lower vnode. If found, the increment the null_node
* reference count (but NOT the lower vnode's VREF counter).
*/
hd = NULL_NHASH(lowervp);
loop:
for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) {
if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
vp = NULLTOV(a);
/*
* We need vget for the VXLOCK
* stuff, but we don't want to lock
* the lower node.
*/
if (vget(vp, 0, p)) {
printf ("null_node_find: vget failed.\n");
goto loop;
};
return (vp);
}
}
return NULL;
}
/*
* Make a new null_node node.
* Vp is the alias vnode, lofsvp is the lower vnode.
* Maintain a reference to (lowervp).
*/
static int
null_node_alloc(mp, lowervp, vpp)
struct mount *mp;
struct vnode *lowervp;
struct vnode **vpp;
{
struct null_node_hashhead *hd;
struct null_node *xp;
struct vnode *othervp, *vp;
int error;
if (error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp))
return (error);
vp = *vpp;
MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
vp->v_type = lowervp->v_type;
xp->null_vnode = vp;
vp->v_data = xp;
xp->null_lowervp = lowervp;
/*
* Before we insert our new node onto the hash chains,
* check to see if someone else has beaten us to it.
* (We could have slept in MALLOC.)
*/
if (othervp = null_node_find(lowervp)) {
FREE(xp, M_TEMP);
vp->v_type = VBAD; /* node is discarded */
vp->v_usecount = 0; /* XXX */
*vpp = othervp;
return 0;
};
VREF(lowervp); /* Extra VREF will be vrele'd in null_node_create */
hd = NULL_NHASH(lowervp);
LIST_INSERT_HEAD(hd, xp, null_hash);
return 0;
}
/*
* Try to find an existing null_node vnode refering
* to it, otherwise make a new null_node vnode which
* contains a reference to the lower vnode.
*/
int
null_node_create(mp, lowervp, newvpp)
struct mount *mp;
struct vnode *lowervp;
struct vnode **newvpp;
{
struct vnode *aliasvp;
if (aliasvp = null_node_find(mp, lowervp)) {
/*
* null_node_find has taken another reference
* to the alias vnode.
*/
#ifdef NULLFS_DIAGNOSTIC
vprint("null_node_create: exists", NULLTOV(ap));
#endif
/* VREF(aliasvp); --- done in null_node_find */
} else {
int error;
/*
* Get new vnode.
*/
#ifdef NULLFS_DIAGNOSTIC
printf("null_node_create: create new alias vnode\n");
#endif
/*
* Make new vnode reference the null_node.
*/
if (error = null_node_alloc(mp, lowervp, &aliasvp))
return error;
/*
* aliasvp is already VREF'd by getnewvnode()
*/
}
vrele(lowervp);
#ifdef DIAGNOSTIC
if (lowervp->v_usecount < 1) {
/* Should never happen... */
vprint ("null_node_create: alias ", aliasvp);
vprint ("null_node_create: lower ", lowervp);
panic ("null_node_create: lower has 0 usecount.");
};
#endif
#ifdef NULLFS_DIAGNOSTIC
vprint("null_node_create: alias", aliasvp);
vprint("null_node_create: lower", lowervp);
#endif
*newvpp = aliasvp;
return (0);
}
#ifdef NULLFS_DIAGNOSTIC
struct vnode *
null_checkvp(vp, fil, lno)
struct vnode *vp;
char *fil;
int lno;
{
struct null_node *a = VTONULL(vp);
#ifdef notyet
/*
* Can't do this check because vop_reclaim runs
* with a funny vop vector.
*/
if (vp->v_op != null_vnodeop_p) {
printf ("null_checkvp: on non-null-node\n");
while (null_checkvp_barrier) /*WAIT*/ ;
panic("null_checkvp");
};
#endif
if (a->null_lowervp == NULL) {
/* Should never happen */
int i; u_long *p;
printf("vp = %x, ZERO ptr\n", vp);
for (p = (u_long *) a, i = 0; i < 8; i++)
printf(" %x", p[i]);
printf("\n");
/* wait for debugger */
while (null_checkvp_barrier) /*WAIT*/ ;
panic("null_checkvp");
}
if (a->null_lowervp->v_usecount < 1) {
int i; u_long *p;
printf("vp = %x, unref'ed lowervp\n", vp);
for (p = (u_long *) a, i = 0; i < 8; i++)
printf(" %x", p[i]);
printf("\n");
/* wait for debugger */
while (null_checkvp_barrier) /*WAIT*/ ;
panic ("null with unref'ed lowervp");
};
#ifdef notyet
printf("null %x/%d -> %x/%d [%s, %d]\n",
NULLTOV(a), NULLTOV(a)->v_usecount,
a->null_lowervp, a->null_lowervp->v_usecount,
fil, lno);
#endif
return a->null_lowervp;
}
#endif

View file

@ -0,0 +1,367 @@
/*
* 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. 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.
*
* @(#)null_vfsops.c 8.7 (Berkeley) 5/14/95
*
* @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
* $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
*/
/*
* Null Layer
* (See null_vnops.c for a description of what this does.)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <miscfs/nullfs/null.h>
/*
* Mount null layer
*/
int
nullfs_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
int error = 0;
struct null_args args;
struct vnode *lowerrootvp, *vp;
struct vnode *nullm_rootvp;
struct null_mount *xmp;
u_int size;
#ifdef NULLFS_DIAGNOSTIC
printf("nullfs_mount(mp = %x)\n", mp);
#endif
/*
* Update is a no-op
*/
if (mp->mnt_flag & MNT_UPDATE) {
return (EOPNOTSUPP);
/* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/
}
/*
* Get argument
*/
if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args)))
return (error);
/*
* Find lower node
*/
NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
UIO_USERSPACE, args.target, p);
if (error = namei(ndp))
return (error);
/*
* Sanity check on lower vnode
*/
lowerrootvp = ndp->ni_vp;
vrele(ndp->ni_dvp);
ndp->ni_dvp = NULL;
xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
M_UFSMNT, M_WAITOK); /* XXX */
/*
* Save reference to underlying FS
*/
xmp->nullm_vfs = lowerrootvp->v_mount;
/*
* Save reference. Each mount also holds
* a reference on the root vnode.
*/
error = null_node_create(mp, lowerrootvp, &vp);
/*
* Unlock the node (either the lower or the alias)
*/
VOP_UNLOCK(vp, 0, p);
/*
* Make sure the node alias worked
*/
if (error) {
vrele(lowerrootvp);
free(xmp, M_UFSMNT); /* XXX */
return (error);
}
/*
* Keep a held reference to the root vnode.
* It is vrele'd in nullfs_unmount.
*/
nullm_rootvp = vp;
nullm_rootvp->v_flag |= VROOT;
xmp->nullm_rootvp = nullm_rootvp;
if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = (qaddr_t) xmp;
vfs_getnewfsid(mp);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
(void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
&size);
bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
#ifdef NULLFS_DIAGNOSTIC
printf("nullfs_mount: lower %s, alias at %s\n",
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
#endif
return (0);
}
/*
* VFS start. Nothing needed here - the start routine
* on the underlying filesystem will have been called
* when that filesystem was mounted.
*/
int
nullfs_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
/* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */
}
/*
* Free reference to null layer
*/
int
nullfs_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
int error;
int flags = 0;
#ifdef NULLFS_DIAGNOSTIC
printf("nullfs_unmount(mp = %x)\n", mp);
#endif
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
/*
* Clear out buffer cache. I don't think we
* ever get anything cached at this level at the
* moment, but who knows...
*/
#if 0
mntflushbuf(mp, 0);
if (mntinvalbuf(mp, 1))
return (EBUSY);
#endif
if (nullm_rootvp->v_usecount > 1)
return (EBUSY);
if (error = vflush(mp, nullm_rootvp, flags))
return (error);
#ifdef NULLFS_DIAGNOSTIC
vprint("alias root of lower", nullm_rootvp);
#endif
/*
* Release reference on underlying root vnode
*/
vrele(nullm_rootvp);
/*
* And blow it away for future re-use
*/
vgone(nullm_rootvp);
/*
* Finally, throw away the null_mount structure
*/
free(mp->mnt_data, M_UFSMNT); /* XXX */
mp->mnt_data = 0;
return 0;
}
int
nullfs_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct vnode *vp;
#ifdef NULLFS_DIAGNOSTIC
printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp,
MOUNTTONULLMOUNT(mp)->nullm_rootvp,
NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
);
#endif
/*
* Return locked reference to root.
*/
vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
VREF(vp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
*vpp = vp;
return 0;
}
int
nullfs_quotactl(mp, cmd, uid, arg, p)
struct mount *mp;
int cmd;
uid_t uid;
caddr_t arg;
struct proc *p;
{
return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p);
}
int
nullfs_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
int error;
struct statfs mstat;
#ifdef NULLFS_DIAGNOSTIC
printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp,
MOUNTTONULLMOUNT(mp)->nullm_rootvp,
NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
);
#endif
bzero(&mstat, sizeof(mstat));
error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p);
if (error)
return (error);
/* now copy across the "interesting" information and fake the rest */
sbp->f_type = mstat.f_type;
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
sbp->f_blocks = mstat.f_blocks;
sbp->f_bfree = mstat.f_bfree;
sbp->f_bavail = mstat.f_bavail;
sbp->f_files = mstat.f_files;
sbp->f_ffree = mstat.f_ffree;
if (sbp != &mp->mnt_stat) {
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
int
nullfs_sync(mp, waitfor, cred, p)
struct mount *mp;
int waitfor;
struct ucred *cred;
struct proc *p;
{
/*
* XXX - Assumes no data cached at null layer.
*/
return (0);
}
int
nullfs_vget(mp, ino, vpp)
struct mount *mp;
ino_t ino;
struct vnode **vpp;
{
return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp);
}
int
nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
struct mount *mp;
struct fid *fidp;
struct mbuf *nam;
struct vnode **vpp;
int *exflagsp;
struct ucred**credanonp;
{
return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp);
}
int
nullfs_vptofh(vp, fhp)
struct vnode *vp;
struct fid *fhp;
{
return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp);
}
int nullfs_init __P((struct vfsconf *));
#define nullfs_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
struct vfsops null_vfsops = {
nullfs_mount,
nullfs_start,
nullfs_unmount,
nullfs_root,
nullfs_quotactl,
nullfs_statfs,
nullfs_sync,
nullfs_vget,
nullfs_fhtovp,
nullfs_vptofh,
nullfs_init,
nullfs_sysctl,
};

View file

@ -0,0 +1,645 @@
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* John Heidemann of the UCLA Ficus project.
*
* 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.
*
* @(#)null_vnops.c 8.6 (Berkeley) 5/27/95
*
* Ancestors:
* @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
* $Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
* ...and...
* @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
*/
/*
* Null Layer
*
* (See mount_null(8) for more information.)
*
* The null layer duplicates a portion of the file system
* name space under a new name. In this respect, it is
* similar to the loopback file system. It differs from
* the loopback fs in two respects: it is implemented using
* a stackable layers techniques, and it's "null-node"s stack above
* all lower-layer vnodes, not just over directory vnodes.
*
* The null layer has two purposes. First, it serves as a demonstration
* of layering by proving a layer which does nothing. (It actually
* does everything the loopback file system does, which is slightly
* more than nothing.) Second, the null layer can serve as a prototype
* layer. Since it provides all necessary layer framework,
* new file system layers can be created very easily be starting
* with a null layer.
*
* The remainder of this man page examines the null layer as a basis
* for constructing new layers.
*
*
* INSTANTIATING NEW NULL LAYERS
*
* New null layers are created with mount_null(8).
* Mount_null(8) takes two arguments, the pathname
* of the lower vfs (target-pn) and the pathname where the null
* layer will appear in the namespace (alias-pn). After
* the null layer is put into place, the contents
* of target-pn subtree will be aliased under alias-pn.
*
*
* OPERATION OF A NULL LAYER
*
* The null layer is the minimum file system layer,
* simply bypassing all possible operations to the lower layer
* for processing there. The majority of its activity centers
* on the bypass routine, though which nearly all vnode operations
* pass.
*
* The bypass routine accepts arbitrary vnode operations for
* handling by the lower layer. It begins by examing vnode
* operation arguments and replacing any null-nodes by their
* lower-layer equivlants. It then invokes the operation
* on the lower layer. Finally, it replaces the null-nodes
* in the arguments and, if a vnode is return by the operation,
* stacks a null-node on top of the returned vnode.
*
* Although bypass handles most operations, vop_getattr, vop_lock,
* vop_unlock, vop_inactive, vop_reclaim, and vop_print are not
* bypassed. Vop_getattr must change the fsid being returned.
* Vop_lock and vop_unlock must handle any locking for the
* current vnode as well as pass the lock request down.
* Vop_inactive and vop_reclaim are not bypassed so that
* they can handle freeing null-layer specific data. Vop_print
* is not bypassed to avoid excessive debugging information.
* Also, certain vnode operations change the locking state within
* the operation (create, mknod, remove, link, rename, mkdir, rmdir,
* and symlink). Ideally these operations should not change the
* lock state, but should be changed to let the caller of the
* function unlock them. Otherwise all intermediate vnode layers
* (such as union, umapfs, etc) must catch these functions to do
* the necessary locking at their layer.
*
*
* INSTANTIATING VNODE STACKS
*
* Mounting associates the null layer with a lower layer,
* effect stacking two VFSes. Vnode stacks are instead
* created on demand as files are accessed.
*
* The initial mount creates a single vnode stack for the
* root of the new null layer. All other vnode stacks
* are created as a result of vnode operations on
* this or other null vnode stacks.
*
* New vnode stacks come into existance as a result of
* an operation which returns a vnode.
* The bypass routine stacks a null-node above the new
* vnode before returning it to the caller.
*
* For example, imagine mounting a null layer with
* "mount_null /usr/include /dev/layer/null".
* Changing directory to /dev/layer/null will assign
* the root null-node (which was created when the null layer was mounted).
* Now consider opening "sys". A vop_lookup would be
* done on the root null-node. This operation would bypass through
* to the lower layer which would return a vnode representing
* the UFS "sys". Null_bypass then builds a null-node
* aliasing the UFS "sys" and returns this to the caller.
* Later operations on the null-node "sys" will repeat this
* process when constructing other vnode stacks.
*
*
* CREATING OTHER FILE SYSTEM LAYERS
*
* One of the easiest ways to construct new file system layers is to make
* a copy of the null layer, rename all files and variables, and
* then begin modifing the copy. Sed can be used to easily rename
* all variables.
*
* The umap layer is an example of a layer descended from the
* null layer.
*
*
* INVOKING OPERATIONS ON LOWER LAYERS
*
* There are two techniques to invoke operations on a lower layer
* when the operation cannot be completely bypassed. Each method
* is appropriate in different situations. In both cases,
* it is the responsibility of the aliasing layer to make
* the operation arguments "correct" for the lower layer
* by mapping an vnode arguments to the lower layer.
*
* The first approach is to call the aliasing layer's bypass routine.
* This method is most suitable when you wish to invoke the operation
* currently being hanldled on the lower layer. It has the advantage
* that the bypass routine already must do argument mapping.
* An example of this is null_getattrs in the null layer.
*
* A second approach is to directly invoked vnode operations on
* the lower layer with the VOP_OPERATIONNAME interface.
* The advantage of this method is that it is easy to invoke
* arbitrary operations on the lower layer. The disadvantage
* is that vnodes arguments must be manualy mapped.
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <miscfs/nullfs/null.h>
int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
/*
* This is the 10-Apr-92 bypass routine.
* This version has been optimized for speed, throwing away some
* safety checks. It should still always work, but it's not as
* robust to programmer errors.
* Define SAFETY to include some error checking code.
*
* In general, we map all vnodes going down and unmap them on the way back.
* As an exception to this, vnodes can be marked "unmapped" by setting
* the Nth bit in operation's vdesc_flags.
*
* Also, some BSD vnode operations have the side effect of vrele'ing
* their arguments. With stacking, the reference counts are held
* by the upper node, not the lower one, so we must handle these
* side-effects here. This is not of concern in Sun-derived systems
* since there are no such side-effects.
*
* This makes the following assumptions:
* - only one returned vpp
* - no INOUT vpp's (Sun's vop_open has one of these)
* - the vnode operation vector of the first vnode should be used
* to determine what implementation of the op should be invoked
* - all mapped vnodes are of our vnode-type (NEEDSWORK:
* problems on rmdir'ing mount points and renaming?)
*/
int
null_bypass(ap)
struct vop_generic_args /* {
struct vnodeop_desc *a_desc;
<other random data follows, presumably>
} */ *ap;
{
extern int (**null_vnodeop_p)(); /* not extern, really "forward" */
register struct vnode **this_vp_p;
int error;
struct vnode *old_vps[VDESC_MAX_VPS];
struct vnode **vps_p[VDESC_MAX_VPS];
struct vnode ***vppp;
struct vnodeop_desc *descp = ap->a_desc;
int reles, i;
if (null_bug_bypass)
printf ("null_bypass: %s\n", descp->vdesc_name);
#ifdef SAFETY
/*
* We require at least one vp.
*/
if (descp->vdesc_vp_offsets == NULL ||
descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
panic ("null_bypass: no vp's in map.\n");
#endif
/*
* Map the vnodes going in.
* Later, we'll invoke the operation based on
* the first mapped vnode's operation vector.
*/
reles = descp->vdesc_flags;
for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
break; /* bail out at end of list */
vps_p[i] = this_vp_p =
VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
/*
* We're not guaranteed that any but the first vnode
* are of our type. Check for and don't map any
* that aren't. (We must always map first vp or vclean fails.)
*/
if (i && (*this_vp_p == NULL ||
(*this_vp_p)->v_op != null_vnodeop_p)) {
old_vps[i] = NULL;
} else {
old_vps[i] = *this_vp_p;
*(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p);
/*
* XXX - Several operations have the side effect
* of vrele'ing their vp's. We must account for
* that. (This should go away in the future.)
*/
if (reles & 1)
VREF(*this_vp_p);
}
}
/*
* Call the operation on the lower layer
* with the modified argument structure.
*/
error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
/*
* Maintain the illusion of call-by-value
* by restoring vnodes in the argument structure
* to their original value.
*/
reles = descp->vdesc_flags;
for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
break; /* bail out at end of list */
if (old_vps[i]) {
*(vps_p[i]) = old_vps[i];
if (reles & 1)
vrele(*(vps_p[i]));
}
}
/*
* Map the possible out-going vpp
* (Assumes that the lower layer always returns
* a VREF'ed vpp unless it gets an error.)
*/
if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
!(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
!error) {
/*
* XXX - even though some ops have vpp returned vp's,
* several ops actually vrele this before returning.
* We must avoid these ops.
* (This should go away when these ops are regularized.)
*/
if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
goto out;
vppp = VOPARG_OFFSETTO(struct vnode***,
descp->vdesc_vpp_offset,ap);
error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp);
}
out:
return (error);
}
/*
* We have to carry on the locking protocol on the null layer vnodes
* as we progress through the tree. We also have to enforce read-only
* if this layer is mounted read-only.
*/
null_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
struct componentname *cnp = ap->a_cnp;
struct proc *p = cnp->cn_proc;
int flags = cnp->cn_flags;
struct vop_lock_args lockargs;
struct vop_unlock_args unlockargs;
struct vnode *dvp, *vp;
int error;
if ((flags & ISLASTCN) && (ap->a_dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return (EROFS);
error = null_bypass(ap);
if (error == EJUSTRETURN && (flags & ISLASTCN) &&
(ap->a_dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME))
error = EROFS;
/*
* We must do the same locking and unlocking at this layer as
* is done in the layers below us. We could figure this out
* based on the error return and the LASTCN, LOCKPARENT, and
* LOCKLEAF flags. However, it is more expidient to just find
* out the state of the lower level vnodes and set ours to the
* same state.
*/
dvp = ap->a_dvp;
vp = *ap->a_vpp;
if (dvp == vp)
return (error);
if (!VOP_ISLOCKED(dvp)) {
unlockargs.a_vp = dvp;
unlockargs.a_flags = 0;
unlockargs.a_p = p;
vop_nounlock(&unlockargs);
}
if (vp != NULL && VOP_ISLOCKED(vp)) {
lockargs.a_vp = vp;
lockargs.a_flags = LK_SHARED;
lockargs.a_p = p;
vop_nolock(&lockargs);
}
return (error);
}
/*
* Setattr call. Disallow write attempts if the layer is mounted read-only.
*/
int
null_setattr(ap)
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;
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
vap->va_gid != (gid_t)VNOVAL || vap->va_atime.ts_sec != VNOVAL ||
vap->va_mtime.ts_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&
(vp->v_mount->mnt_flag & MNT_RDONLY))
return (EROFS);
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
return (EISDIR);
case VCHR:
case VBLK:
case VSOCK:
case VFIFO:
return (0);
case VREG:
case VLNK:
default:
/*
* Disallow write attempts if the filesystem is
* mounted read-only.
*/
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
}
}
return (null_bypass(ap));
}
/*
* We handle getattr only to change the fsid.
*/
int
null_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
int error;
if (error = null_bypass(ap))
return (error);
/* Requires that arguments be restored. */
ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
return (0);
}
int
null_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
mode_t mode = ap->a_mode;
/*
* Disallow write attempts on read-only layers;
* unless the file is a socket, fifo, or a block or
* character device resident on the file system.
*/
if (mode & VWRITE) {
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (vp->v_mount->mnt_flag & MNT_RDONLY)
return (EROFS);
break;
}
}
return (null_bypass(ap));
}
/*
* We need to process our own vnode lock and then clear the
* interlock flag as it applies only to our vnode, not the
* vnodes below us on the stack.
*/
int
null_lock(ap)
struct vop_lock_args /* {
struct vnode *a_vp;
int a_flags;
struct proc *a_p;
} */ *ap;
{
vop_nolock(ap);
if ((ap->a_flags & LK_TYPE_MASK) == LK_DRAIN)
return (0);
ap->a_flags &= ~LK_INTERLOCK;
return (null_bypass(ap));
}
/*
* We need to process our own vnode unlock and then clear the
* interlock flag as it applies only to our vnode, not the
* vnodes below us on the stack.
*/
int
null_unlock(ap)
struct vop_unlock_args /* {
struct vnode *a_vp;
int a_flags;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
vop_nounlock(ap);
ap->a_flags &= ~LK_INTERLOCK;
return (null_bypass(ap));
}
int
null_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
/*
* Do nothing (and _don't_ bypass).
* Wait to vrele lowervp until reclaim,
* so that until then our null_node is in the
* cache and reusable.
*
* NEEDSWORK: Someday, consider inactive'ing
* the lowervp and then trying to reactivate it
* with capabilities (v_id)
* like they do in the name lookup cache code.
* That's too much work for now.
*/
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return (0);
}
int
null_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct null_node *xp = VTONULL(vp);
struct vnode *lowervp = xp->null_lowervp;
/*
* Note: in vop_reclaim, vp->v_op == dead_vnodeop_p,
* so we can't call VOPs on ourself.
*/
/* After this assignment, this node will not be re-used. */
xp->null_lowervp = NULL;
LIST_REMOVE(xp, null_hash);
FREE(vp->v_data, M_TEMP);
vp->v_data = NULL;
vrele (lowervp);
return (0);
}
int
null_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp));
return (0);
}
/*
* XXX - vop_strategy must be hand coded because it has no
* vnode in its arguments.
* This goes away with a merged VM/buffer cache.
*/
int
null_strategy(ap)
struct vop_strategy_args /* {
struct buf *a_bp;
} */ *ap;
{
struct buf *bp = ap->a_bp;
int error;
struct vnode *savedvp;
savedvp = bp->b_vp;
bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
error = VOP_STRATEGY(bp);
bp->b_vp = savedvp;
return (error);
}
/*
* XXX - like vop_strategy, vop_bwrite must be hand coded because it has no
* vnode in its arguments.
* This goes away with a merged VM/buffer cache.
*/
int
null_bwrite(ap)
struct vop_bwrite_args /* {
struct buf *a_bp;
} */ *ap;
{
struct buf *bp = ap->a_bp;
int error;
struct vnode *savedvp;
savedvp = bp->b_vp;
bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
error = VOP_BWRITE(bp);
bp->b_vp = savedvp;
return (error);
}
/*
* Global vfs data structures
*/
int (**null_vnodeop_p)();
struct vnodeopv_entry_desc null_vnodeop_entries[] = {
{ &vop_default_desc, null_bypass },
{ &vop_lookup_desc, null_lookup },
{ &vop_setattr_desc, null_setattr },
{ &vop_getattr_desc, null_getattr },
{ &vop_access_desc, null_access },
{ &vop_lock_desc, null_lock },
{ &vop_unlock_desc, null_unlock },
{ &vop_inactive_desc, null_inactive },
{ &vop_reclaim_desc, null_reclaim },
{ &vop_print_desc, null_print },
{ &vop_strategy_desc, null_strategy },
{ &vop_bwrite_desc, null_bwrite },
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc null_vnodeop_opv_desc =
{ &null_vnodeop_p, null_vnodeop_entries };

View file

@ -0,0 +1,272 @@
/*
* 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. 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.
*
* @(#)portal_vfsops.c 8.11 (Berkeley) 5/14/95
*
* $Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp jsp $
*/
/*
* Portal Filesystem
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/file.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/un.h>
#include <miscfs/portal/portal.h>
int
portal_init(vfsp)
struct vfsconf *vfsp;
{
return (0);
}
/*
* Mount the per-process file descriptors (/dev/fd)
*/
int
portal_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
struct file *fp;
struct portal_args args;
struct portalmount *fmp;
struct socket *so;
struct vnode *rvp;
u_int size;
int error;
/*
* Update is a no-op
*/
if (mp->mnt_flag & MNT_UPDATE)
return (EOPNOTSUPP);
if (error = copyin(data, (caddr_t) &args, sizeof(struct portal_args)))
return (error);
if (error = getsock(p->p_fd, args.pa_socket, &fp))
return (error);
so = (struct socket *) fp->f_data;
if (so->so_proto->pr_domain->dom_family != AF_UNIX)
return (ESOCKTNOSUPPORT);
error = getnewvnode(VT_PORTAL, mp, portal_vnodeop_p, &rvp); /* XXX */
if (error)
return (error);
MALLOC(rvp->v_data, void *, sizeof(struct portalnode),
M_TEMP, M_WAITOK);
fmp = (struct portalmount *) malloc(sizeof(struct portalmount),
M_UFSMNT, M_WAITOK); /* XXX */
rvp->v_type = VDIR;
rvp->v_flag |= VROOT;
VTOPORTAL(rvp)->pt_arg = 0;
VTOPORTAL(rvp)->pt_size = 0;
VTOPORTAL(rvp)->pt_fileid = PORTAL_ROOTFILEID;
fmp->pm_root = rvp;
fmp->pm_server = fp; fp->f_count++;
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = (qaddr_t) fmp;
vfs_getnewfsid(mp);
(void)copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
(void)copyinstr(args.pa_config,
mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
#ifdef notdef
bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
bcopy("portal", mp->mnt_stat.f_mntfromname, sizeof("portal"));
#endif
return (0);
}
int
portal_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
}
int
portal_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct vnode *rootvp = VFSTOPORTAL(mp)->pm_root;
int error, flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
/*
* Clear out buffer cache. I don't think we
* ever get anything cached at this level at the
* moment, but who knows...
*/
#ifdef notyet
mntflushbuf(mp, 0);
if (mntinvalbuf(mp, 1))
return (EBUSY);
#endif
if (rootvp->v_usecount > 1)
return (EBUSY);
if (error = vflush(mp, rootvp, flags))
return (error);
/*
* Release reference on underlying root vnode
*/
vrele(rootvp);
/*
* And blow it away for future re-use
*/
vgone(rootvp);
/*
* Shutdown the socket. This will cause the select in the
* daemon to wake up, and then the accept will get ECONNABORTED
* which it interprets as a request to go and bury itself.
*/
soshutdown((struct socket *) VFSTOPORTAL(mp)->pm_server->f_data, 2);
/*
* Discard reference to underlying file. Must call closef because
* this may be the last reference.
*/
closef(VFSTOPORTAL(mp)->pm_server, (struct proc *) 0);
/*
* Finally, throw away the portalmount structure
*/
free(mp->mnt_data, M_UFSMNT); /* XXX */
mp->mnt_data = 0;
return (0);
}
int
portal_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct vnode *vp;
/*
* Return locked reference to root.
*/
vp = VFSTOPORTAL(mp)->pm_root;
VREF(vp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
*vpp = vp;
return (0);
}
int
portal_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
sbp->f_flags = 0;
sbp->f_bsize = 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_files = 1; /* Allow for "." */
sbp->f_ffree = 0; /* See comments above */
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
#define portal_fhtovp ((int (*) __P((struct mount *, struct fid *, \
struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
#define portal_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
struct proc *)))eopnotsupp)
#define portal_sync ((int (*) __P((struct mount *, int, struct ucred *, \
struct proc *)))nullop)
#define portal_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
#define portal_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
eopnotsupp)
#define portal_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
struct vfsops portal_vfsops = {
portal_mount,
portal_start,
portal_unmount,
portal_root,
portal_quotactl,
portal_statfs,
portal_sync,
portal_vget,
portal_fhtovp,
portal_vptofh,
portal_init,
portal_sysctl,
};

View file

@ -0,0 +1,730 @@
/*
* Copyright (c) 1992, 1993
* 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. 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.
*
* @(#)portal_vnops.c 8.14 (Berkeley) 5/21/95
*
* $Id: portal_vnops.c,v 1.4 1992/05/30 10:05:24 jsp Exp jsp $
*/
/*
* Portal Filesystem
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/un.h>
#include <sys/unpcb.h>
#include <miscfs/portal/portal.h>
static int portal_fileid = PORTAL_ROOTFILEID+1;
static void
portal_closefd(p, fd)
struct proc *p;
int fd;
{
int error;
struct {
int fd;
} ua;
int rc;
ua.fd = fd;
error = close(p, &ua, &rc);
/*
* We should never get an error, and there isn't anything
* we could do if we got one, so just print a message.
*/
if (error)
printf("portal_closefd: error = %d\n", error);
}
/*
* vp is the current namei directory
* cnp is the name to locate in that directory...
*/
int
portal_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
struct componentname *cnp = ap->a_cnp;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
char *pname = cnp->cn_nameptr;
struct portalnode *pt;
int error;
struct vnode *fvp = 0;
char *path;
int size;
*vpp = NULLVP;
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
return (EROFS);
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
VREF(dvp);
/*VOP_LOCK(dvp);*/
return (0);
}
error = getnewvnode(VT_PORTAL, dvp->v_mount, portal_vnodeop_p, &fvp);
if (error)
goto bad;
fvp->v_type = VREG;
MALLOC(fvp->v_data, void *, sizeof(struct portalnode), M_TEMP,
M_WAITOK);
pt = VTOPORTAL(fvp);
/*
* Save all of the remaining pathname and
* advance the namei next pointer to the end
* of the string.
*/
for (size = 0, path = pname; *path; path++)
size++;
cnp->cn_consume = size - cnp->cn_namelen;
pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK);
pt->pt_size = size+1;
bcopy(pname, pt->pt_arg, pt->pt_size);
pt->pt_fileid = portal_fileid++;
*vpp = fvp;
/*VOP_LOCK(fvp);*/
return (0);
bad:;
if (fvp)
vrele(fvp);
return (error);
}
static int
portal_connect(so, so2)
struct socket *so;
struct socket *so2;
{
/* from unp_connect, bypassing the namei stuff... */
struct socket *so3;
struct unpcb *unp2;
struct unpcb *unp3;
if (so2 == 0)
return (ECONNREFUSED);
if (so->so_type != so2->so_type)
return (EPROTOTYPE);
if ((so2->so_options & SO_ACCEPTCONN) == 0)
return (ECONNREFUSED);
if ((so3 = sonewconn(so2, 0)) == 0)
return (ECONNREFUSED);
unp2 = sotounpcb(so2);
unp3 = sotounpcb(so3);
if (unp2->unp_addr)
unp3->unp_addr = m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
so2 = so3;
return (unp_connect2(so, so2));
}
int
portal_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct socket *so = 0;
struct portalnode *pt;
struct proc *p = ap->a_p;
struct vnode *vp = ap->a_vp;
int s;
struct uio auio;
struct iovec aiov[2];
int res;
struct mbuf *cm = 0;
struct cmsghdr *cmsg;
int newfds;
int *ip;
int fd;
int error;
int len;
struct portalmount *fmp;
struct file *fp;
struct portal_cred pcred;
/*
* Nothing to do when opening the root node.
*/
if (vp->v_flag & VROOT)
return (0);
/*
* Can't be opened unless the caller is set up
* to deal with the side effects. Check for this
* by testing whether the p_dupfd has been set.
*/
if (p->p_dupfd >= 0)
return (ENODEV);
pt = VTOPORTAL(vp);
fmp = VFSTOPORTAL(vp->v_mount);
/*
* Create a new socket.
*/
error = socreate(AF_UNIX, &so, SOCK_STREAM, 0);
if (error)
goto bad;
/*
* Reserve some buffer space
*/
res = pt->pt_size + sizeof(pcred) + 512; /* XXX */
error = soreserve(so, res, res);
if (error)
goto bad;
/*
* Kick off connection
*/
error = portal_connect(so, (struct socket *)fmp->pm_server->f_data);
if (error)
goto bad;
/*
* Wait for connection to complete
*/
/*
* XXX: Since the mount point is holding a reference on the
* underlying server socket, it is not easy to find out whether
* the server process is still running. To handle this problem
* we loop waiting for the new socket to be connected (something
* which will only happen if the server is still running) or for
* the reference count on the server socket to drop to 1, which
* will happen if the server dies. Sleep for 5 second intervals
* and keep polling the reference count. XXX.
*/
s = splnet();
while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
if (fmp->pm_server->f_count == 1) {
error = ECONNREFUSED;
splx(s);
goto bad;
}
(void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz);
}
splx(s);
if (so->so_error) {
error = so->so_error;
goto bad;
}
/*
* Set miscellaneous flags
*/
so->so_rcv.sb_timeo = 0;
so->so_snd.sb_timeo = 0;
so->so_rcv.sb_flags |= SB_NOINTR;
so->so_snd.sb_flags |= SB_NOINTR;
pcred.pcr_flag = ap->a_mode;
pcred.pcr_uid = ap->a_cred->cr_uid;
pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
aiov[0].iov_base = (caddr_t) &pcred;
aiov[0].iov_len = sizeof(pcred);
aiov[1].iov_base = pt->pt_arg;
aiov[1].iov_len = pt->pt_size;
auio.uio_iov = aiov;
auio.uio_iovcnt = 2;
auio.uio_rw = UIO_WRITE;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_procp = p;
auio.uio_offset = 0;
auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len;
error = sosend(so, (struct mbuf *) 0, &auio,
(struct mbuf *) 0, (struct mbuf *) 0, 0);
if (error)
goto bad;
len = auio.uio_resid = sizeof(int);
do {
struct mbuf *m = 0;
int flags = MSG_WAITALL;
error = soreceive(so, (struct mbuf **) 0, &auio,
&m, &cm, &flags);
if (error)
goto bad;
/*
* Grab an error code from the mbuf.
*/
if (m) {
m = m_pullup(m, sizeof(int)); /* Needed? */
if (m) {
error = *(mtod(m, int *));
m_freem(m);
} else {
error = EINVAL;
}
} else {
if (cm == 0) {
error = ECONNRESET; /* XXX */
#ifdef notdef
break;
#endif
}
}
} while (cm == 0 && auio.uio_resid == len && !error);
if (cm == 0)
goto bad;
if (auio.uio_resid) {
error = 0;
#ifdef notdef
error = EMSGSIZE;
goto bad;
#endif
}
/*
* XXX: Break apart the control message, and retrieve the
* received file descriptor. Note that more than one descriptor
* may have been received, or that the rights chain may have more
* than a single mbuf in it. What to do?
*/
cmsg = mtod(cm, struct cmsghdr *);
newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int);
if (newfds == 0) {
error = ECONNREFUSED;
goto bad;
}
/*
* At this point the rights message consists of a control message
* header, followed by a data region containing a vector of
* integer file descriptors. The fds were allocated by the action
* of receiving the control message.
*/
ip = (int *) (cmsg + 1);
fd = *ip++;
if (newfds > 1) {
/*
* Close extra fds.
*/
int i;
printf("portal_open: %d extra fds\n", newfds - 1);
for (i = 1; i < newfds; i++) {
portal_closefd(p, *ip);
ip++;
}
}
/*
* Check that the mode the file is being opened for is a subset
* of the mode of the existing descriptor.
*/
fp = p->p_fd->fd_ofiles[fd];
if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
portal_closefd(p, fd);
error = EACCES;
goto bad;
}
/*
* Save the dup fd in the proc structure then return the
* special error code (ENXIO) which causes magic things to
* happen in vn_open. The whole concept is, well, hmmm.
*/
p->p_dupfd = fd;
error = ENXIO;
bad:;
/*
* And discard the control message.
*/
if (cm) {
m_freem(cm);
}
if (so) {
soshutdown(so, 2);
soclose(so);
}
return (error);
}
int
portal_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
struct timeval tv;
bzero(vap, sizeof(*vap));
vattr_null(vap);
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
vap->va_size = DEV_BSIZE;
vap->va_blocksize = DEV_BSIZE;
microtime(&tv);
TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime);
vap->va_mtime = vap->va_atime;
vap->va_ctime = vap->va_ctime;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_rdev = 0;
/* vap->va_qbytes = 0; */
vap->va_bytes = 0;
/* vap->va_qsize = 0; */
if (vp->v_flag & VROOT) {
vap->va_type = VDIR;
vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
S_IRGRP|S_IWGRP|S_IXGRP|
S_IROTH|S_IWOTH|S_IXOTH;
vap->va_nlink = 2;
vap->va_fileid = 2;
} else {
vap->va_type = VREG;
vap->va_mode = S_IRUSR|S_IWUSR|
S_IRGRP|S_IWGRP|
S_IROTH|S_IWOTH;
vap->va_nlink = 1;
vap->va_fileid = VTOPORTAL(vp)->pt_fileid;
}
return (0);
}
int
portal_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
/*
* Can't mess with the root vnode
*/
if (ap->a_vp->v_flag & VROOT)
return (EACCES);
return (0);
}
/*
* Fake readdir, just return empty directory.
* It is hard to deal with '.' and '..' so don't bother.
*/
int
portal_readdir(ap)
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
u_long *a_cookies;
int a_ncookies;
} */ *ap;
{
/*
* We don't allow exporting portal mounts, and currently local
* requests do not need cookies.
*/
if (ap->a_ncookies)
panic("portal_readdir: not hungry");
return (0);
}
int
portal_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return (0);
}
int
portal_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct portalnode *pt = VTOPORTAL(ap->a_vp);
if (pt->pt_arg) {
free((caddr_t) pt->pt_arg, M_TEMP);
pt->pt_arg = 0;
}
FREE(ap->a_vp->v_data, M_TEMP);
ap->a_vp->v_data = 0;
return (0);
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
portal_pathconf(ap)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
int *a_retval;
} */ *ap;
{
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);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* Print out the contents of a Portal vnode.
*/
/* ARGSUSED */
int
portal_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
printf("tag VT_PORTAL, portal vnode\n");
return (0);
}
/*void*/
int
portal_vfree(ap)
struct vop_vfree_args /* {
struct vnode *a_pvp;
ino_t a_ino;
int a_mode;
} */ *ap;
{
return (0);
}
/*
* Portal vnode unsupported operation
*/
int
portal_enotsupp()
{
return (EOPNOTSUPP);
}
/*
* Portal "should never get here" operation
*/
int
portal_badop()
{
panic("portal: bad op");
/* NOTREACHED */
}
/*
* Portal vnode null operation
*/
int
portal_nullop()
{
return (0);
}
#define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
#define portal_mknod ((int (*) __P((struct vop_mknod_args *)))portal_enotsupp)
#define portal_close ((int (*) __P((struct vop_close_args *)))nullop)
#define portal_access ((int (*) __P((struct vop_access_args *)))nullop)
#define portal_read ((int (*) __P((struct vop_read_args *)))portal_enotsupp)
#define portal_write ((int (*) __P((struct vop_write_args *)))portal_enotsupp)
#define portal_ioctl ((int (*) __P((struct vop_ioctl_args *)))portal_enotsupp)
#define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
#define portal_mmap ((int (*) __P((struct vop_mmap_args *)))portal_enotsupp)
#define portal_revoke vop_revoke
#define portal_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
#define portal_seek ((int (*) __P((struct vop_seek_args *)))nullop)
#define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
#define portal_link ((int (*) __P((struct vop_link_args *)))portal_enotsupp)
#define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
#define portal_mkdir ((int (*) __P((struct vop_mkdir_args *)))portal_enotsupp)
#define portal_rmdir ((int (*) __P((struct vop_rmdir_args *)))portal_enotsupp)
#define portal_symlink \
((int (*) __P((struct vop_symlink_args *)))portal_enotsupp)
#define portal_readlink \
((int (*) __P((struct vop_readlink_args *)))portal_enotsupp)
#define portal_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
#define portal_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock)
#define portal_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
#define portal_bmap ((int (*) __P((struct vop_bmap_args *)))portal_badop)
#define portal_strategy \
((int (*) __P((struct vop_strategy_args *)))portal_badop)
#define portal_islocked \
((int (*) __P((struct vop_islocked_args *)))vop_noislocked)
#define fifo_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked)
#define portal_advlock \
((int (*) __P((struct vop_advlock_args *)))portal_enotsupp)
#define portal_blkatoff \
((int (*) __P((struct vop_blkatoff_args *)))portal_enotsupp)
#define portal_valloc ((int(*) __P(( \
struct vnode *pvp, \
int mode, \
struct ucred *cred, \
struct vnode **vpp))) portal_enotsupp)
#define portal_truncate \
((int (*) __P((struct vop_truncate_args *)))portal_enotsupp)
#define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
#define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
int (**portal_vnodeop_p)();
struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, portal_lookup }, /* lookup */
{ &vop_create_desc, portal_create }, /* create */
{ &vop_mknod_desc, portal_mknod }, /* mknod */
{ &vop_open_desc, portal_open }, /* open */
{ &vop_close_desc, portal_close }, /* close */
{ &vop_access_desc, portal_access }, /* access */
{ &vop_getattr_desc, portal_getattr }, /* getattr */
{ &vop_setattr_desc, portal_setattr }, /* setattr */
{ &vop_read_desc, portal_read }, /* read */
{ &vop_write_desc, portal_write }, /* write */
{ &vop_ioctl_desc, portal_ioctl }, /* ioctl */
{ &vop_select_desc, portal_select }, /* select */
{ &vop_mmap_desc, portal_mmap }, /* mmap */
{ &vop_revoke_desc, portal_revoke }, /* revoke */
{ &vop_fsync_desc, portal_fsync }, /* fsync */
{ &vop_seek_desc, portal_seek }, /* seek */
{ &vop_remove_desc, portal_remove }, /* remove */
{ &vop_link_desc, portal_link }, /* link */
{ &vop_rename_desc, portal_rename }, /* rename */
{ &vop_mkdir_desc, portal_mkdir }, /* mkdir */
{ &vop_rmdir_desc, portal_rmdir }, /* rmdir */
{ &vop_symlink_desc, portal_symlink }, /* symlink */
{ &vop_readdir_desc, portal_readdir }, /* readdir */
{ &vop_readlink_desc, portal_readlink }, /* readlink */
{ &vop_abortop_desc, portal_abortop }, /* abortop */
{ &vop_inactive_desc, portal_inactive }, /* inactive */
{ &vop_reclaim_desc, portal_reclaim }, /* reclaim */
{ &vop_lock_desc, portal_lock }, /* lock */
{ &vop_unlock_desc, portal_unlock }, /* unlock */
{ &vop_bmap_desc, portal_bmap }, /* bmap */
{ &vop_strategy_desc, portal_strategy }, /* strategy */
{ &vop_print_desc, portal_print }, /* print */
{ &vop_islocked_desc, portal_islocked }, /* islocked */
{ &vop_pathconf_desc, portal_pathconf }, /* pathconf */
{ &vop_advlock_desc, portal_advlock }, /* advlock */
{ &vop_blkatoff_desc, portal_blkatoff }, /* blkatoff */
{ &vop_valloc_desc, portal_valloc }, /* valloc */
{ &vop_vfree_desc, portal_vfree }, /* vfree */
{ &vop_truncate_desc, portal_truncate }, /* truncate */
{ &vop_update_desc, portal_update }, /* update */
{ &vop_bwrite_desc, portal_bwrite }, /* bwrite */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc portal_vnodeop_opv_desc =
{ &portal_vnodeop_p, portal_vnodeop_entries };

194
sys/miscfs/procfs/procfs.h Normal file
View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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.h 8.9 (Berkeley) 5/14/95
*
* From:
* $Id: procfs.h,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
/*
* The different types of node in a procfs filesystem
*/
typedef enum {
Proot, /* the filesystem root */
Pcurproc, /* symbolic link for curproc */
Pproc, /* a process-specific sub-directory */
Pfile, /* the executable file */
Pmem, /* the process's memory image */
Pregs, /* the process's register set */
Pfpregs, /* the process's FP register set */
Pctl, /* process control */
Pstatus, /* process status */
Pnote, /* process notifier */
Pnotepg /* process group notifier */
} pfstype;
/*
* control data for the proc file system.
*/
struct pfsnode {
struct pfsnode *pfs_next; /* next on list */
struct vnode *pfs_vnode; /* vnode associated with this pfsnode */
pfstype pfs_type; /* type of procfs node */
pid_t pfs_pid; /* associated process */
u_short pfs_mode; /* mode bits for stat() */
u_long pfs_flags; /* open flags */
u_long pfs_fileno; /* unique file id */
};
#define PROCFS_NOTELEN 64 /* max length of a note (/proc/$pid/note) */
#define PROCFS_CTLLEN 8 /* max length of a ctl msg (/proc/$pid/ctl */
/*
* Kernel stuff follows
*/
#ifdef KERNEL
#define CNEQ(cnp, s, len) \
((cnp)->cn_namelen == (len) && \
(bcmp((s), (cnp)->cn_nameptr, (len)) == 0))
/*
* Format of a directory entry in /proc, ...
* This must map onto struct dirent (see <dirent.h>)
*/
#define PROCFS_NAMELEN 8
struct pfsdent {
u_long d_fileno;
u_short d_reclen;
u_char d_type;
u_char d_namlen;
char d_name[PROCFS_NAMELEN];
};
#define UIO_MX sizeof(struct pfsdent)
#define PROCFS_FILENO(pid, type) \
(((type) < Pproc) ? \
((type) + 2) : \
((((pid)+1) << 4) + ((int) (type))))
/*
* Convert between pfsnode vnode
*/
#define VTOPFS(vp) ((struct pfsnode *)(vp)->v_data)
#define PFSTOV(pfs) ((pfs)->pfs_vnode)
typedef struct vfs_namemap vfs_namemap_t;
struct vfs_namemap {
const char *nm_name;
int nm_val;
};
int vfs_getuserstr __P((struct uio *, char *, int *));
vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int));
/* <machine/reg.h> */
struct reg;
struct fpreg;
#define PFIND(pid) ((pid) ? pfind(pid) : &proc0)
int procfs_freevp __P((struct vnode *));
int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype));
struct vnode *procfs_findtextvp __P((struct proc *));
int procfs_sstep __P((struct proc *, int));
void procfs_fix_sstep __P((struct proc *));
int procfs_read_regs __P((struct proc *, struct reg *));
int procfs_write_regs __P((struct proc *, struct reg *));
int procfs_read_fpregs __P((struct proc *, struct fpreg *));
int procfs_write_fpregs __P((struct proc *, struct fpreg *));
int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
/* functions to check whether or not files should be displayed */
int procfs_validfile __P((struct proc *));
int procfs_validfpregs __P((struct proc *));
int procfs_validregs __P((struct proc *));
#define PROCFS_LOCKED 0x01
#define PROCFS_WANT 0x02
extern int (**procfs_vnodeop_p)();
extern struct vfsops procfs_vfsops;
/*
* Prototypes for procfs vnode ops
*/
int procfs_badop(); /* varargs */
int procfs_rw __P((struct vop_read_args *));
int procfs_lookup __P((struct vop_lookup_args *));
#define procfs_create ((int (*) __P((struct vop_create_args *))) procfs_badop)
#define procfs_mknod ((int (*) __P((struct vop_mknod_args *))) procfs_badop)
int procfs_open __P((struct vop_open_args *));
int procfs_close __P((struct vop_close_args *));
int procfs_access __P((struct vop_access_args *));
int procfs_getattr __P((struct vop_getattr_args *));
int procfs_setattr __P((struct vop_setattr_args *));
#define procfs_read procfs_rw
#define procfs_write procfs_rw
int procfs_ioctl __P((struct vop_ioctl_args *));
#define procfs_select ((int (*) __P((struct vop_select_args *))) procfs_badop)
#define procfs_mmap ((int (*) __P((struct vop_mmap_args *))) procfs_badop)
#define procfs_revoke vop_revoke
#define procfs_fsync ((int (*) __P((struct vop_fsync_args *))) procfs_badop)
#define procfs_seek ((int (*) __P((struct vop_seek_args *))) procfs_badop)
#define procfs_remove ((int (*) __P((struct vop_remove_args *))) procfs_badop)
#define procfs_link ((int (*) __P((struct vop_link_args *))) procfs_badop)
#define procfs_rename ((int (*) __P((struct vop_rename_args *))) procfs_badop)
#define procfs_mkdir ((int (*) __P((struct vop_mkdir_args *))) procfs_badop)
#define procfs_rmdir ((int (*) __P((struct vop_rmdir_args *))) procfs_badop)
#define procfs_symlink ((int (*) __P((struct vop_symlink_args *))) procfs_badop)
int procfs_readdir __P((struct vop_readdir_args *));
int procfs_readlink __P((struct vop_readlink_args *));
int procfs_abortop __P((struct vop_abortop_args *));
int procfs_inactive __P((struct vop_inactive_args *));
int procfs_reclaim __P((struct vop_reclaim_args *));
#define procfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock)
#define procfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
int procfs_bmap __P((struct vop_bmap_args *));
#define procfs_strategy ((int (*) __P((struct vop_strategy_args *))) procfs_badop)
int procfs_print __P((struct vop_print_args *));
#define procfs_islocked \
((int (*) __P((struct vop_islocked_args *)))vop_noislocked)
#define procfs_advlock ((int (*) __P((struct vop_advlock_args *))) procfs_badop)
#define procfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *))) procfs_badop)
#define procfs_valloc ((int (*) __P((struct vop_valloc_args *))) procfs_badop)
#define procfs_vfree ((int (*) __P((struct vop_vfree_args *))) nullop)
#define procfs_truncate ((int (*) __P((struct vop_truncate_args *))) procfs_badop)
#define procfs_update ((int (*) __P((struct vop_update_args *))) nullop)
#endif /* KERNEL */

View file

@ -0,0 +1,300 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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_ctl.c 8.4 (Berkeley) 6/15/94
*
* From:
* $Id: procfs_ctl.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <sys/ptrace.h>
#include <miscfs/procfs/procfs.h>
#ifndef FIX_SSTEP
#define FIX_SSTEP(p)
#endif
/*
* True iff process (p) is in trace wait state
* relative to process (curp)
*/
#define TRACE_WAIT_P(curp, p) \
((p)->p_stat == SSTOP && \
(p)->p_pptr == (curp) && \
((p)->p_flag & P_TRACED))
#define PROCFS_CTL_ATTACH 1
#define PROCFS_CTL_DETACH 2
#define PROCFS_CTL_STEP 3
#define PROCFS_CTL_RUN 4
#define PROCFS_CTL_WAIT 5
static vfs_namemap_t ctlnames[] = {
/* special /proc commands */
{ "attach", PROCFS_CTL_ATTACH },
{ "detach", PROCFS_CTL_DETACH },
{ "step", PROCFS_CTL_STEP },
{ "run", PROCFS_CTL_RUN },
{ "wait", PROCFS_CTL_WAIT },
{ 0 },
};
static vfs_namemap_t signames[] = {
/* regular signal names */
{ "hup", SIGHUP }, { "int", SIGINT },
{ "quit", SIGQUIT }, { "ill", SIGILL },
{ "trap", SIGTRAP }, { "abrt", SIGABRT },
{ "iot", SIGIOT }, { "emt", SIGEMT },
{ "fpe", SIGFPE }, { "kill", SIGKILL },
{ "bus", SIGBUS }, { "segv", SIGSEGV },
{ "sys", SIGSYS }, { "pipe", SIGPIPE },
{ "alrm", SIGALRM }, { "term", SIGTERM },
{ "urg", SIGURG }, { "stop", SIGSTOP },
{ "tstp", SIGTSTP }, { "cont", SIGCONT },
{ "chld", SIGCHLD }, { "ttin", SIGTTIN },
{ "ttou", SIGTTOU }, { "io", SIGIO },
{ "xcpu", SIGXCPU }, { "xfsz", SIGXFSZ },
{ "vtalrm", SIGVTALRM }, { "prof", SIGPROF },
{ "winch", SIGWINCH }, { "info", SIGINFO },
{ "usr1", SIGUSR1 }, { "usr2", SIGUSR2 },
{ 0 },
};
static int
procfs_control(curp, p, op)
struct proc *curp;
struct proc *p;
int op;
{
int error;
/*
* Attach - attaches the target process for debugging
* by the calling process.
*/
if (op == PROCFS_CTL_ATTACH) {
/* check whether already being traced */
if (p->p_flag & P_TRACED)
return (EBUSY);
/* can't trace yourself! */
if (p->p_pid == curp->p_pid)
return (EINVAL);
/*
* Go ahead and set the trace flag.
* Save the old parent (it's reset in
* _DETACH, and also in kern_exit.c:wait4()
* Reparent the process so that the tracing
* proc gets to see all the action.
* Stop the target.
*/
p->p_flag |= P_TRACED;
p->p_xstat = 0; /* XXX ? */
if (p->p_pptr != curp) {
p->p_oppid = p->p_pptr->p_pid;
proc_reparent(p, curp);
}
psignal(p, SIGSTOP);
return (0);
}
/*
* Target process must be stopped, owned by (curp) and
* be set up for tracing (P_TRACED flag set).
* Allow DETACH to take place at any time for sanity.
* Allow WAIT any time, of course.
*/
switch (op) {
case PROCFS_CTL_DETACH:
case PROCFS_CTL_WAIT:
break;
default:
if (!TRACE_WAIT_P(curp, p))
return (EBUSY);
}
/*
* do single-step fixup if needed
*/
FIX_SSTEP(p);
/*
* Don't deliver any signal by default.
* To continue with a signal, just send
* the signal name to the ctl file
*/
p->p_xstat = 0;
switch (op) {
/*
* Detach. Cleans up the target process, reparent it if possible
* and set it running once more.
*/
case PROCFS_CTL_DETACH:
/* if not being traced, then this is a painless no-op */
if ((p->p_flag & P_TRACED) == 0)
return (0);
/* not being traced any more */
p->p_flag &= ~P_TRACED;
/* give process back to original parent */
if (p->p_oppid != p->p_pptr->p_pid) {
struct proc *pp;
pp = pfind(p->p_oppid);
if (pp)
proc_reparent(p, pp);
}
p->p_oppid = 0;
p->p_flag &= ~P_WAITED; /* XXX ? */
wakeup((caddr_t) curp); /* XXX for CTL_WAIT below ? */
break;
/*
* Step. Let the target process execute a single instruction.
*/
case PROCFS_CTL_STEP:
if (error = procfs_sstep(p, 1))
return (error);
break;
/*
* Run. Let the target process continue running until a breakpoint
* or some other trap.
*/
case PROCFS_CTL_RUN:
break;
/*
* Wait for the target process to stop.
* If the target is not being traced then just wait
* to enter
*/
case PROCFS_CTL_WAIT:
error = 0;
if (p->p_flag & P_TRACED) {
while (error == 0 &&
(p->p_stat != SSTOP) &&
(p->p_flag & P_TRACED) &&
(p->p_pptr == curp)) {
error = tsleep((caddr_t) p,
PWAIT|PCATCH, "procfsx", 0);
}
if (error == 0 && !TRACE_WAIT_P(curp, p))
error = EBUSY;
} else {
while (error == 0 && p->p_stat != SSTOP) {
error = tsleep((caddr_t) p,
PWAIT|PCATCH, "procfs", 0);
}
}
return (error);
default:
panic("procfs_control");
}
if (p->p_stat == SSTOP)
setrunnable(p);
return (0);
}
int
procfs_doctl(curp, p, pfs, uio)
struct proc *curp;
struct pfsnode *pfs;
struct uio *uio;
struct proc *p;
{
int xlen;
int error;
char msg[PROCFS_CTLLEN+1];
vfs_namemap_t *nm;
if (uio->uio_rw != UIO_WRITE)
return (EOPNOTSUPP);
xlen = PROCFS_CTLLEN;
error = vfs_getuserstr(uio, msg, &xlen);
if (error)
return (error);
/*
* Map signal names into signal generation
* or debug control. Unknown commands and/or signals
* return EOPNOTSUPP.
*
* Sending a signal while the process is being debugged
* also has the side effect of letting the target continue
* to run. There is no way to single-step a signal delivery.
*/
error = EOPNOTSUPP;
nm = vfs_findname(ctlnames, msg, xlen);
if (nm) {
error = procfs_control(curp, p, nm->nm_val);
} else {
nm = vfs_findname(signames, msg, xlen);
if (nm) {
if (TRACE_WAIT_P(curp, p)) {
p->p_xstat = nm->nm_val;
FIX_SSTEP(p);
setrunnable(p);
} else {
psignal(p, nm->nm_val);
}
error = 0;
}
}
return (error);
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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_fpregs.c 8.2 (Berkeley) 6/15/94
*
* From:
* $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <machine/reg.h>
#include <miscfs/procfs/procfs.h>
int
procfs_dofpregs(curp, p, pfs, uio)
struct proc *curp;
struct proc *p;
struct pfsnode *pfs;
struct uio *uio;
{
int error;
struct fpreg r;
char *kv;
int kl;
kl = sizeof(r);
kv = (char *) &r;
kv += uio->uio_offset;
kl -= uio->uio_offset;
if (kl > uio->uio_resid)
kl = uio->uio_resid;
if (kl < 0)
error = EINVAL;
else
error = procfs_read_fpregs(p, &r);
if (error == 0)
error = uiomove(kv, kl, uio);
if (error == 0 && uio->uio_rw == UIO_WRITE) {
if (p->p_stat != SSTOP)
error = EBUSY;
else
error = procfs_write_fpregs(p, &r);
}
uio->uio_offset = 0;
return (error);
}
int
procfs_validfpregs(p)
struct proc *p;
{
return ((p->p_flag & P_SYSTEM) == 0);
}

View file

@ -0,0 +1,300 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* Copyright (c) 1993 Sean Eric Fagan
* 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 and Sean Eric Fagan.
*
* 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_mem.c 8.5 (Berkeley) 6/15/94
*
* From:
* $Id: procfs_mem.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
/*
* This is a lightly hacked and merged version
* of sef's pread/pwrite functions
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <miscfs/procfs/procfs.h>
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
static int
procfs_rwmem(p, uio)
struct proc *p;
struct uio *uio;
{
int error;
int writing;
writing = uio->uio_rw == UIO_WRITE;
/*
* Only map in one page at a time. We don't have to, but it
* makes things easier. This way is trivial - right?
*/
do {
vm_map_t map, tmap;
vm_object_t object;
vm_offset_t kva;
vm_offset_t uva;
int page_offset; /* offset into page */
vm_offset_t pageno; /* page number */
vm_map_entry_t out_entry;
vm_prot_t out_prot;
vm_page_t m;
boolean_t wired, single_use;
vm_offset_t off;
u_int len;
int fix_prot;
uva = (vm_offset_t) uio->uio_offset;
if (uva > VM_MAXUSER_ADDRESS) {
error = 0;
break;
}
/*
* Get the page number of this segment.
*/
pageno = trunc_page(uva);
page_offset = uva - pageno;
/*
* How many bytes to copy
*/
len = min(PAGE_SIZE - page_offset, uio->uio_resid);
/*
* The map we want...
*/
map = &p->p_vmspace->vm_map;
/*
* Check the permissions for the area we're interested
* in.
*/
fix_prot = 0;
if (writing)
fix_prot = !vm_map_check_protection(map, pageno,
pageno + PAGE_SIZE, VM_PROT_WRITE);
if (fix_prot) {
/*
* If the page is not writable, we make it so.
* XXX It is possible that a page may *not* be
* read/executable, if a process changes that!
* We will assume, for now, that a page is either
* VM_PROT_ALL, or VM_PROT_READ|VM_PROT_EXECUTE.
*/
error = vm_map_protect(map, pageno,
pageno + PAGE_SIZE, VM_PROT_ALL, 0);
if (error)
break;
}
/*
* Now we need to get the page. out_entry, out_prot, wired,
* and single_use aren't used. One would think the vm code
* would be a *bit* nicer... We use tmap because
* vm_map_lookup() can change the map argument.
*/
tmap = map;
error = vm_map_lookup(&tmap, pageno,
writing ? VM_PROT_WRITE : VM_PROT_READ,
&out_entry, &object, &off, &out_prot,
&wired, &single_use);
/*
* We're done with tmap now.
*/
if (!error)
vm_map_lookup_done(tmap, out_entry);
/*
* Fault the page in...
*/
if (!error && writing && object->shadow) {
m = vm_page_lookup(object, off);
if (m == 0 || (m->flags & PG_COPYONWRITE))
error = vm_fault(map, pageno,
VM_PROT_WRITE, FALSE);
}
/* Find space in kernel_map for the page we're interested in */
if (!error)
error = vm_map_find(kernel_map, object, off, &kva,
PAGE_SIZE, 1);
if (!error) {
/*
* Neither vm_map_lookup() nor vm_map_find() appear
* to add a reference count to the object, so we do
* that here and now.
*/
vm_object_reference(object);
/*
* Mark the page we just found as pageable.
*/
error = vm_map_pageable(kernel_map, kva,
kva + PAGE_SIZE, 0);
/*
* Now do the i/o move.
*/
if (!error)
error = uiomove(kva + page_offset, len, uio);
vm_map_remove(kernel_map, kva, kva + PAGE_SIZE);
}
if (fix_prot)
vm_map_protect(map, pageno, pageno + PAGE_SIZE,
VM_PROT_READ|VM_PROT_EXECUTE, 0);
} while (error == 0 && uio->uio_resid > 0);
return (error);
}
/*
* Copy data in and out of the target process.
* We do this by mapping the process's page into
* the kernel and then doing a uiomove direct
* from the kernel address space.
*/
int
procfs_domem(curp, p, pfs, uio)
struct proc *curp;
struct proc *p;
struct pfsnode *pfs;
struct uio *uio;
{
if (uio->uio_resid == 0)
return (0);
return (procfs_rwmem(p, uio));
}
/*
* Given process (p), find the vnode from which
* it's text segment is being executed.
*
* It would be nice to grab this information from
* the VM system, however, there is no sure-fire
* way of doing that. Instead, fork(), exec() and
* wait() all maintain the p_textvp field in the
* process proc structure which contains a held
* reference to the exec'ed vnode.
*/
struct vnode *
procfs_findtextvp(p)
struct proc *p;
{
return (p->p_textvp);
}
#ifdef probably_never
/*
* Given process (p), find the vnode from which
* it's text segment is being mapped.
*
* (This is here, rather than in procfs_subr in order
* to keep all the VM related code in one place.)
*/
struct vnode *
procfs_findtextvp(p)
struct proc *p;
{
int error;
vm_object_t object;
vm_offset_t pageno; /* page number */
/* find a vnode pager for the user address space */
for (pageno = VM_MIN_ADDRESS;
pageno < VM_MAXUSER_ADDRESS;
pageno += PAGE_SIZE) {
vm_map_t map;
vm_map_entry_t out_entry;
vm_prot_t out_prot;
boolean_t wired, single_use;
vm_offset_t off;
map = &p->p_vmspace->vm_map;
error = vm_map_lookup(&map, pageno,
VM_PROT_READ,
&out_entry, &object, &off, &out_prot,
&wired, &single_use);
if (!error) {
vm_pager_t pager;
printf("procfs: found vm object\n");
vm_map_lookup_done(map, out_entry);
printf("procfs: vm object = %x\n", object);
/*
* At this point, assuming no errors, object
* is the VM object mapping UVA (pageno).
* Ensure it has a vnode pager, then grab
* the vnode from that pager's handle.
*/
pager = object->pager;
printf("procfs: pager = %x\n", pager);
if (pager)
printf("procfs: found pager, type = %d\n", pager->pg_type);
if (pager && pager->pg_type == PG_VNODE) {
struct vnode *vp;
vp = (struct vnode *) pager->pg_handle;
printf("procfs: vp = 0x%x\n", vp);
return (vp);
}
}
}
printf("procfs: text object not found\n");
return (0);
}
#endif /* probably_never */

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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_regs.c 8.4 (Berkeley) 6/15/94
*
* From:
* $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <machine/reg.h>
#include <miscfs/procfs/procfs.h>
int
procfs_doregs(curp, p, pfs, uio)
struct proc *curp;
struct proc *p;
struct pfsnode *pfs;
struct uio *uio;
{
int error;
struct reg r;
char *kv;
int kl;
kl = sizeof(r);
kv = (char *) &r;
kv += uio->uio_offset;
kl -= uio->uio_offset;
if (kl > uio->uio_resid)
kl = uio->uio_resid;
if (kl < 0)
error = EINVAL;
else
error = procfs_read_regs(p, &r);
if (error == 0)
error = uiomove(kv, kl, uio);
if (error == 0 && uio->uio_rw == UIO_WRITE) {
if (p->p_stat != SSTOP)
error = EBUSY;
else
error = procfs_write_regs(p, &r);
}
uio->uio_offset = 0;
return (error);
}
int
procfs_validregs(p)
struct proc *p;
{
return ((p->p_flag & P_SYSTEM) == 0);
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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_status.c 8.4 (Berkeley) 6/15/94
*
* From:
* $Id: procfs_status.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <miscfs/procfs/procfs.h>
int
procfs_dostatus(curp, p, pfs, uio)
struct proc *curp;
struct proc *p;
struct pfsnode *pfs;
struct uio *uio;
{
struct session *sess;
struct tty *tp;
struct ucred *cr;
char *ps;
char *sep;
int pid, ppid, pgid, sid;
int i;
int xlen;
int error;
char psbuf[256]; /* XXX - conservative */
if (uio->uio_rw != UIO_READ)
return (EOPNOTSUPP);
pid = p->p_pid;
ppid = p->p_pptr ? p->p_pptr->p_pid : 0,
pgid = p->p_pgrp->pg_id;
sess = p->p_pgrp->pg_session;
sid = sess->s_leader ? sess->s_leader->p_pid : 0;
/* comm pid ppid pgid sid maj,min ctty,sldr start ut st wmsg uid groups ... */
ps = psbuf;
bcopy(p->p_comm, ps, MAXCOMLEN);
ps[MAXCOMLEN] = '\0';
ps += strlen(ps);
ps += sprintf(ps, " %d %d %d %d ", pid, ppid, pgid, sid);
if ((p->p_flag&P_CONTROLT) && (tp = sess->s_ttyp))
ps += sprintf(ps, "%d,%d ", major(tp->t_dev), minor(tp->t_dev));
else
ps += sprintf(ps, "%d,%d ", -1, -1);
sep = "";
if (sess->s_ttyvp) {
ps += sprintf(ps, "%sctty", sep);
sep = ",";
}
if (SESS_LEADER(p)) {
ps += sprintf(ps, "%ssldr", sep);
sep = ",";
}
if (*sep != ',')
ps += sprintf(ps, "noflags");
if (p->p_flag & P_INMEM)
ps += sprintf(ps, " %d,%d",
p->p_stats->p_start.tv_sec,
p->p_stats->p_start.tv_usec);
else
ps += sprintf(ps, " -1,-1");
{
struct timeval ut, st;
calcru(p, &ut, &st, (void *) 0);
ps += sprintf(ps, " %d,%d %d,%d",
ut.tv_sec,
ut.tv_usec,
st.tv_sec,
st.tv_usec);
}
ps += sprintf(ps, " %s",
(p->p_wchan && p->p_wmesg) ? p->p_wmesg : "nochan");
cr = p->p_ucred;
ps += sprintf(ps, " %d", cr->cr_uid);
for (i = 0; i < cr->cr_ngroups; i++)
ps += sprintf(ps, ",%d", cr->cr_groups[i]);
ps += sprintf(ps, "\n");
xlen = ps - psbuf;
xlen -= uio->uio_offset;
ps = psbuf + uio->uio_offset;
xlen = imin(xlen, uio->uio_resid);
if (xlen <= 0)
error = 0;
else
error = uiomove(ps, xlen, uio);
return (error);
}

View file

@ -0,0 +1,314 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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
*
* From:
* $Id: procfs_subr.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <miscfs/procfs/procfs.h>
static struct pfsnode *pfshead;
static int pfsvplock;
/*
* allocate a pfsnode/vnode pair. the vnode is
* referenced, but not locked.
*
* the pid, pfs_type, and mount point uniquely
* identify a pfsnode. the mount point is needed
* because someone might mount this filesystem
* twice.
*
* all pfsnodes 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 process 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
procfs_allocvp(mp, vpp, pid, pfs_type)
struct mount *mp;
struct vnode **vpp;
long pid;
pfstype pfs_type;
{
struct proc *p = curproc; /* XXX */
struct pfsnode *pfs;
struct vnode *vp;
struct pfsnode **pp;
int error;
loop:
for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
vp = PFSTOV(pfs);
if (pfs->pfs_pid == pid &&
pfs->pfs_type == pfs_type &&
vp->v_mount == mp) {
if (vget(vp, 0, p))
goto loop;
*vpp = vp;
return (0);
}
}
/*
* otherwise lock the vp list while we call getnewvnode
* since that can block.
*/
if (pfsvplock & PROCFS_LOCKED) {
pfsvplock |= PROCFS_WANT;
sleep((caddr_t) &pfsvplock, PINOD);
goto loop;
}
pfsvplock |= PROCFS_LOCKED;
if (error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp))
goto out;
vp = *vpp;
MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK);
vp->v_data = pfs;
pfs->pfs_next = 0;
pfs->pfs_pid = (pid_t) pid;
pfs->pfs_type = pfs_type;
pfs->pfs_vnode = vp;
pfs->pfs_flags = 0;
pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
switch (pfs_type) {
case Proot: /* /proc = dr-xr-xr-x */
pfs->pfs_mode = (VREAD|VEXEC) |
(VREAD|VEXEC) >> 3 |
(VREAD|VEXEC) >> 6;
vp->v_type = VDIR;
vp->v_flag = VROOT;
break;
case Pcurproc: /* /proc/curproc = lr--r--r-- */
pfs->pfs_mode = (VREAD) |
(VREAD >> 3) |
(VREAD >> 6);
vp->v_type = VLNK;
break;
case Pproc:
pfs->pfs_mode = (VREAD|VEXEC) |
(VREAD|VEXEC) >> 3 |
(VREAD|VEXEC) >> 6;
vp->v_type = VDIR;
break;
case Pfile:
case Pmem:
case Pregs:
case Pfpregs:
pfs->pfs_mode = (VREAD|VWRITE);
vp->v_type = VREG;
break;
case Pctl:
case Pnote:
case Pnotepg:
pfs->pfs_mode = (VWRITE);
vp->v_type = VREG;
break;
case Pstatus:
pfs->pfs_mode = (VREAD) |
(VREAD >> 3) |
(VREAD >> 6);
vp->v_type = VREG;
break;
default:
panic("procfs_allocvp");
}
/* add to procfs vnode list */
for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next)
continue;
*pp = pfs;
out:
pfsvplock &= ~PROCFS_LOCKED;
if (pfsvplock & PROCFS_WANT) {
pfsvplock &= ~PROCFS_WANT;
wakeup((caddr_t) &pfsvplock);
}
return (error);
}
int
procfs_freevp(vp)
struct vnode *vp;
{
struct pfsnode **pfspp;
struct pfsnode *pfs = VTOPFS(vp);
for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) {
if (*pfspp == pfs) {
*pfspp = pfs->pfs_next;
break;
}
}
FREE(vp->v_data, M_TEMP);
vp->v_data = 0;
return (0);
}
int
procfs_rw(ap)
struct vop_read_args *ap;
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct proc *curp = uio->uio_procp;
struct pfsnode *pfs = VTOPFS(vp);
struct proc *p;
p = PFIND(pfs->pfs_pid);
if (p == 0)
return (EINVAL);
switch (pfs->pfs_type) {
case Pnote:
case Pnotepg:
return (procfs_donote(curp, p, pfs, uio));
case Pregs:
return (procfs_doregs(curp, p, pfs, uio));
case Pfpregs:
return (procfs_dofpregs(curp, p, pfs, uio));
case Pctl:
return (procfs_doctl(curp, p, pfs, uio));
case Pstatus:
return (procfs_dostatus(curp, p, pfs, uio));
case Pmem:
return (procfs_domem(curp, p, pfs, uio));
default:
return (EOPNOTSUPP);
}
}
/*
* Get a string from userland into (buf). Strip a trailing
* nl character (to allow easy access from the shell).
* The buffer should be *buflenp + 1 chars long. vfs_getuserstr
* will automatically add a nul char at the end.
*
* Returns 0 on success or the following errors
*
* EINVAL: file offset is non-zero.
* EMSGSIZE: message is longer than kernel buffer
* EFAULT: user i/o buffer is not addressable
*/
int
vfs_getuserstr(uio, buf, buflenp)
struct uio *uio;
char *buf;
int *buflenp;
{
int xlen;
int error;
if (uio->uio_offset != 0)
return (EINVAL);
xlen = *buflenp;
/* must be able to read the whole string in one go */
if (xlen < uio->uio_resid)
return (EMSGSIZE);
xlen = uio->uio_resid;
if (error = uiomove(buf, xlen, uio))
return (error);
/* allow multiple writes without seeks */
uio->uio_offset = 0;
/* cleanup string and remove trailing newline */
buf[xlen] = '\0';
xlen = strlen(buf);
if (xlen > 0 && buf[xlen-1] == '\n')
buf[--xlen] = '\0';
*buflenp = xlen;
return (0);
}
vfs_namemap_t *
vfs_findname(nm, buf, buflen)
vfs_namemap_t *nm;
char *buf;
int buflen;
{
for (; nm->nm_name; nm++)
if (bcmp(buf, (char *) nm->nm_name, buflen+1) == 0)
return (nm);
return (0);
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 1993 Jan-Simon Pendry
* 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. 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_vfsops.c 8.7 (Berkeley) 5/10/95
*
* From:
* $Id: procfs_vfsops.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
*/
/*
* procfs VFS interface
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/syslog.h>
#include <sys/mount.h>
#include <sys/signalvar.h>
#include <sys/vnode.h>
#include <miscfs/procfs/procfs.h>
#include <vm/vm.h> /* for PAGE_SIZE */
/*
* VFS Operations.
*
* mount system call
*/
/* ARGSUSED */
procfs_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
u_int size;
if (UIO_MX & (UIO_MX-1)) {
log(LOG_ERR, "procfs: invalid directory entry size");
return (EINVAL);
}
if (mp->mnt_flag & MNT_UPDATE)
return (EOPNOTSUPP);
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = 0;
vfs_getnewfsid(mp);
(void) copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
size = sizeof("procfs") - 1;
bcopy("procfs", mp->mnt_stat.f_mntfromname, size);
bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
return (0);
}
/*
* unmount system call
*/
procfs_unmount(mp, mntflags, p)
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))
return (error);
return (0);
}
procfs_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
return (procfs_allocvp(mp, vpp, 0, Proot));
}
/* ARGSUSED */
procfs_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
}
/*
* Get file system statistics.
*/
procfs_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
sbp->f_bsize = PAGE_SIZE;
sbp->f_iosize = PAGE_SIZE;
sbp->f_blocks = 1; /* avoid divide by zero in some df's */
sbp->f_bfree = 0;
sbp->f_bavail = 0;
sbp->f_files = maxproc; /* approx */
sbp->f_ffree = maxproc - nprocs; /* approx */
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
procfs_init(vfsp)
struct vfsconf *vfsp;
{
return (0);
}
#define procfs_fhtovp ((int (*) __P((struct mount *, struct fid *, \
struct mbuf *, struct vnode **, int *, struct ucred **)))einval)
#define procfs_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
struct proc *)))eopnotsupp)
#define procfs_sync ((int (*) __P((struct mount *, int, struct ucred *, \
struct proc *)))nullop)
#define procfs_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
#define procfs_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
eopnotsupp)
#define procfs_vptofh ((int (*) __P((struct vnode *, struct fid *)))einval)
struct vfsops procfs_vfsops = {
procfs_mount,
procfs_start,
procfs_unmount,
procfs_root,
procfs_quotactl,
procfs_statfs,
procfs_sync,
procfs_vget,
procfs_fhtovp,
procfs_vptofh,
procfs_init,
procfs_sysctl,
};

View file

@ -0,0 +1,929 @@
/*
* Copyright (c) 1993, 1995 Jan-Simon Pendry
* 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. 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
*
* From:
* $Id: procfs_vnops.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
*/
/*
* procfs vnode interface
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/dirent.h>
#include <sys/resourcevar.h>
#include <vm/vm.h> /* for PAGE_SIZE */
#include <machine/reg.h>
#include <miscfs/procfs/procfs.h>
/*
* Vnode Operations.
*
*/
/*
* This is a list of the valid names in the
* process-specific sub-directories. It is
* used in procfs_lookup and procfs_readdir
*/
struct proc_target {
u_char pt_type;
u_char pt_namlen;
char *pt_name;
pfstype pt_pfstype;
int (*pt_valid) __P((struct proc *p));
} proc_targets[] = {
#define N(s) sizeof(s)-1, s
/* name type validp */
{ DT_DIR, N("."), Pproc, NULL },
{ DT_DIR, N(".."), Proot, NULL },
{ DT_REG, N("file"), Pfile, procfs_validfile },
{ DT_REG, N("mem"), Pmem, NULL },
{ DT_REG, N("regs"), Pregs, procfs_validregs },
{ DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs },
{ DT_REG, N("ctl"), Pctl, NULL },
{ DT_REG, N("status"), Pstatus, NULL },
{ DT_REG, N("note"), Pnote, NULL },
{ DT_REG, N("notepg"), Pnotepg, NULL },
#undef N
};
static int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]);
static pid_t atopid __P((const char *, u_int));
/*
* set things up for doing i/o on
* the pfsnode (vp). (vp) is locked
* on entry, and should be left locked
* on exit.
*
* for procfs we don't need to do anything
* in particular for i/o. all that is done
* is to support exclusive open on process
* memory images.
*/
procfs_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
switch (pfs->pfs_type) {
case Pmem:
if (PFIND(pfs->pfs_pid) == 0)
return (ENOENT); /* was ESRCH, jsp */
if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) ||
(pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))
return (EBUSY);
if (ap->a_mode & FWRITE)
pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
return (0);
default:
break;
}
return (0);
}
/*
* close the pfsnode (vp) after doing i/o.
* (vp) is not locked on entry or exit.
*
* nothing to do for procfs other than undo
* any exclusive open flag (see _open above).
*/
procfs_close(ap)
struct vop_close_args /* {
struct vnode *a_vp;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
switch (pfs->pfs_type) {
case Pmem:
if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL))
pfs->pfs_flags &= ~(FWRITE|O_EXCL);
break;
}
return (0);
}
/*
* do an ioctl operation on pfsnode (vp).
* (vp) is not locked on entry or exit.
*/
procfs_ioctl(ap)
struct vop_ioctl_args /* {
struct vnode *a_vp;
int a_command;
caddr_t a_data;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
return (ENOTTY);
}
/*
* do block mapping for pfsnode (vp).
* since we don't use the buffer cache
* for procfs this function should never
* be called. in any case, it's not clear
* what part of the kernel ever makes use
* of this function. for sanity, this is the
* usual no-op bmap, although returning
* (EIO) would be a reasonable alternative.
*/
procfs_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct vnode **a_vpp;
daddr_t *a_bnp;
int *a_runp;
} */ *ap;
{
if (ap->a_vpp != NULL)
*ap->a_vpp = ap->a_vp;
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
return (0);
}
/*
* procfs_inactive is called when the pfsnode
* 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 procfs, check if the process is still
* alive and if it isn't then just throw away
* the vnode by calling vgone(). this may
* be overkill and a waste of time since the
* chances are that the process will still be
* there and PFIND is not free.
*
* (vp) is locked on entry, but must be unlocked on exit.
*/
procfs_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct pfsnode *pfs = VTOPFS(vp);
VOP_UNLOCK(vp, 0, ap->a_p);
if (PFIND(pfs->pfs_pid) == 0)
vgone(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.
*/
procfs_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
return (procfs_freevp(ap->a_vp));
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
procfs_pathconf(ap)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
int *a_retval;
} */ *ap;
{
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);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* _print is used for debugging.
* just print a readable description
* of (vp).
*/
procfs_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
printf("tag VT_PROCFS, type %s, pid %d, mode %x, flags %x\n",
pfs->pfs_type, pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags);
}
/*
* _abortop is called when operations such as
* rename and create fail. this entry is responsible
* for undoing any side-effects caused by the lookup.
* this will always include freeing the pathname buffer.
*/
procfs_abortop(ap)
struct vop_abortop_args /* {
struct vnode *a_dvp;
struct componentname *a_cnp;
} */ *ap;
{
if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
return (0);
}
/*
* generic entry point for unsupported operations
*/
procfs_badop()
{
return (EIO);
}
/*
* Invent attributes for pfsnode (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 procfs.
*/
procfs_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct pfsnode *pfs = VTOPFS(ap->a_vp);
struct vattr *vap = ap->a_vap;
struct proc *procp;
struct timeval tv;
int error;
/* first check the process still exists */
switch (pfs->pfs_type) {
case Proot:
case Pcurproc:
procp = 0;
break;
default:
procp = PFIND(pfs->pfs_pid);
if (procp == 0)
return (ENOENT);
}
error = 0;
/* 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_mode = pfs->pfs_mode;
vap->va_fileid = pfs->pfs_fileno;
vap->va_flags = 0;
vap->va_blocksize = PAGE_SIZE;
vap->va_bytes = vap->va_size = 0;
/*
* Make all times be current TOD.
* It would be possible to get the process start
* time from the p_stat structure, but there's
* no "file creation" time stamp anyway, and the
* p_stat structure is not addressible if u. gets
* swapped out for that process.
*/
microtime(&tv);
TIMEVAL_TO_TIMESPEC(&tv, &vap->va_ctime);
vap->va_atime = vap->va_mtime = vap->va_ctime;
/*
* If the process has exercised some setuid or setgid
* privilege, then rip away read/write permission so
* that only root can gain access.
*/
switch (pfs->pfs_type) {
case Pmem:
case Pregs:
case Pfpregs:
if (procp->p_flag & P_SUGID)
vap->va_mode &= ~((VREAD|VWRITE)|
((VREAD|VWRITE)>>3)|
((VREAD|VWRITE)>>6));
case Pctl:
case Pstatus:
case Pnote:
case Pnotepg:
vap->va_nlink = 1;
vap->va_uid = procp->p_ucred->cr_uid;
vap->va_gid = procp->p_ucred->cr_gid;
break;
}
/*
* now do the object specific fields
*
* The size could be set from struct reg, but it's hardly
* worth the trouble, and it puts some (potentially) machine
* dependent data into this machine-independent code. If it
* becomes important then this function should break out into
* a per-file stat function in the corresponding .c file.
*/
switch (pfs->pfs_type) {
case Proot:
/*
* Set nlink to 1 to tell fts(3) we don't actually know.
*/
vap->va_nlink = 1;
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_size = vap->va_bytes = DEV_BSIZE;
break;
case Pcurproc: {
char buf[16]; /* should be enough */
vap->va_nlink = 1;
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_size = vap->va_bytes =
sprintf(buf, "%ld", (long)curproc->p_pid);
break;
}
case Pproc:
vap->va_nlink = 2;
vap->va_uid = procp->p_ucred->cr_uid;
vap->va_gid = procp->p_ucred->cr_gid;
vap->va_size = vap->va_bytes = DEV_BSIZE;
break;
case Pfile:
error = EOPNOTSUPP;
break;
case Pmem:
vap->va_bytes = vap->va_size =
ctob(procp->p_vmspace->vm_tsize +
procp->p_vmspace->vm_dsize +
procp->p_vmspace->vm_ssize);
break;
case Pregs:
vap->va_bytes = vap->va_size = sizeof(struct reg);
break;
case Pfpregs:
vap->va_bytes = vap->va_size = sizeof(struct fpreg);
break;
case Pctl:
case Pstatus:
case Pnote:
case Pnotepg:
break;
default:
panic("procfs_getattr");
}
return (error);
}
procfs_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
/*
* just fake out attribute setting
* it's not good to generate an error
* return, otherwise things like creat()
* will fail when they try to set the
* file length to 0. worse, this means
* that echo $note > /proc/$pid/note will fail.
*/
return (0);
}
/*
* implement access checking.
*
* something very similar to this code is duplicated
* throughout the 4bsd kernel and should be moved
* into kern/vfs_subr.c sometime.
*
* 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.
*/
procfs_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct vattr *vap;
struct vattr vattr;
int error;
/*
* If you're the super-user,
* you always get access.
*/
if (ap->a_cred->cr_uid == 0)
return (0);
vap = &vattr;
if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p))
return (error);
/*
* Access check is based on only one of owner, group, public.
* If not owner, then check group. If not a member of the
* group, then check public access.
*/
if (ap->a_cred->cr_uid != vap->va_uid) {
gid_t *gp;
int i;
ap->a_mode >>= 3;
gp = ap->a_cred->cr_groups;
for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++)
if (vap->va_gid == *gp)
goto found;
ap->a_mode >>= 3;
found:
;
}
if ((vap->va_mode & ap->a_mode) == ap->a_mode)
return (0);
return (EACCES);
}
/*
* lookup. this is incredibly complicated in the
* general case, however for most pseudo-filesystems
* very little needs to be done.
*
* unless you want to get a migraine, just make sure your
* filesystem doesn't do any locking of its own. otherwise
* read and inwardly digest ufs_lookup().
*/
procfs_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
struct componentname *cnp = ap->a_cnp;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
char *pname = cnp->cn_nameptr;
struct proc *curp = cnp->cn_proc;
int error = 0;
struct proc_target *pt;
struct vnode *fvp;
pid_t pid;
struct pfsnode *pfs;
struct proc *p;
int i;
*vpp = NULL;
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
return (EROFS);
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
VREF(dvp);
/* vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, curp); */
return (0);
}
pfs = VTOPFS(dvp);
switch (pfs->pfs_type) {
case Proot:
if (cnp->cn_flags & ISDOTDOT)
return (EIO);
if (CNEQ(cnp, "curproc", 7))
return (procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc));
pid = atopid(pname, cnp->cn_namelen);
if (pid == NO_PID)
break;
p = PFIND(pid);
if (p == 0)
break;
return (procfs_allocvp(dvp->v_mount, vpp, pid, Pproc));
case Pproc:
if (cnp->cn_flags & ISDOTDOT)
return (procfs_root(dvp->v_mount, vpp));
p = PFIND(pfs->pfs_pid);
if (p == 0)
break;
for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) {
if (cnp->cn_namelen == pt->pt_namlen &&
bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 &&
(pt->pt_valid == NULL || (*pt->pt_valid)(p)))
goto found;
}
break;
found:
if (pt->pt_pfstype == Pfile) {
fvp = procfs_findtextvp(p);
/* We already checked that it exists. */
VREF(fvp);
vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, curp);
*vpp = fvp;
return (0);
}
return (procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid,
pt->pt_pfstype));
default:
return (ENOTDIR);
}
return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS);
}
int
procfs_validfile(p)
struct proc *p;
{
return (procfs_findtextvp(p) != NULLVP);
}
/*
* readdir returns directory entries from pfsnode (vp).
*
* the strategy here with procfs is to generate a single
* directory entry at a time (struct pfsdent) 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 procfs, this is
* hardly worth the added code complexity.
*
* this should just be done through read()
*/
procfs_readdir(ap)
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
u_long *a_cookies;
int a_ncookies;
} */ *ap;
{
struct uio *uio = ap->a_uio;
struct pfsdent d;
struct pfsdent *dp = &d;
struct pfsnode *pfs;
int error;
int count;
int i;
/*
* We don't allow exporting procfs mounts, and currently local
* requests do not need cookies.
*/
if (ap->a_ncookies)
panic("procfs_readdir: not hungry");
pfs = VTOPFS(ap->a_vp);
if (uio->uio_resid < UIO_MX)
return (EINVAL);
if (uio->uio_offset & (UIO_MX-1))
return (EINVAL);
if (uio->uio_offset < 0)
return (EINVAL);
error = 0;
count = 0;
i = uio->uio_offset / UIO_MX;
switch (pfs->pfs_type) {
/*
* this is for the process-specific sub-directories.
* all that is needed to is copy out all the entries
* from the procent[] table (top of this file).
*/
case Pproc: {
struct proc *p;
struct proc_target *pt;
p = PFIND(pfs->pfs_pid);
if (p == NULL)
break;
for (pt = &proc_targets[i];
uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) {
if (pt->pt_valid && (*pt->pt_valid)(p) == 0)
continue;
dp->d_reclen = UIO_MX;
dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype);
dp->d_namlen = pt->pt_namlen;
bcopy(pt->pt_name, dp->d_name, pt->pt_namlen + 1);
dp->d_type = pt->pt_type;
if (error = uiomove((caddr_t)dp, UIO_MX, uio))
break;
}
break;
}
/*
* this is for the root of the procfs filesystem
* what is needed is a special entry for "curproc"
* followed by an entry for each process on allproc
#ifdef PROCFS_ZOMBIE
* and zombproc.
#endif
*/
case Proot: {
#ifdef PROCFS_ZOMBIE
int doingzomb = 0;
#endif
int pcnt = 0;
volatile struct proc *p = allproc.lh_first;
again:
for (; p && uio->uio_resid >= UIO_MX; i++, pcnt++) {
bzero((char *) dp, UIO_MX);
dp->d_reclen = UIO_MX;
switch (i) {
case 0: /* `.' */
case 1: /* `..' */
dp->d_fileno = PROCFS_FILENO(0, Proot);
dp->d_namlen = i + 1;
bcopy("..", dp->d_name, dp->d_namlen);
dp->d_name[i + 1] = '\0';
dp->d_type = DT_DIR;
break;
case 2:
dp->d_fileno = PROCFS_FILENO(0, Pcurproc);
dp->d_namlen = 7;
bcopy("curproc", dp->d_name, 8);
dp->d_type = DT_LNK;
break;
default:
while (pcnt < i) {
pcnt++;
p = p->p_list.le_next;
if (!p)
goto done;
}
dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc);
dp->d_namlen = sprintf(dp->d_name, "%ld",
(long)p->p_pid);
dp->d_type = DT_REG;
p = p->p_list.le_next;
break;
}
if (error = uiomove((caddr_t)dp, UIO_MX, uio))
break;
}
done:
#ifdef PROCFS_ZOMBIE
if (p == 0 && doingzomb == 0) {
doingzomb = 1;
p = zombproc.lh_first;
goto again;
}
#endif
break;
}
default:
error = ENOTDIR;
break;
}
uio->uio_offset = i * UIO_MX;
return (error);
}
/*
* readlink reads the link of `curproc'
*/
procfs_readlink(ap)
struct vop_readlink_args *ap;
{
struct uio *uio = ap->a_uio;
char buf[16]; /* should be enough */
int len;
if (VTOPFS(ap->a_vp)->pfs_fileno != PROCFS_FILENO(0, Pcurproc))
return (EINVAL);
len = sprintf(buf, "%ld", (long)curproc->p_pid);
return (uiomove((caddr_t)buf, len, ap->a_uio));
}
/*
* convert decimal ascii to pid_t
*/
static pid_t
atopid(b, len)
const char *b;
u_int len;
{
pid_t p = 0;
while (len--) {
char c = *b++;
if (c < '0' || c > '9')
return (NO_PID);
p = 10 * p + (c - '0');
if (p > PID_MAX)
return (NO_PID);
}
return (p);
}
/*
* procfs vnode operations.
*/
int (**procfs_vnodeop_p)();
struct vnodeopv_entry_desc procfs_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, procfs_lookup }, /* lookup */
{ &vop_create_desc, procfs_create }, /* create */
{ &vop_mknod_desc, procfs_mknod }, /* mknod */
{ &vop_open_desc, procfs_open }, /* open */
{ &vop_close_desc, procfs_close }, /* close */
{ &vop_access_desc, procfs_access }, /* access */
{ &vop_getattr_desc, procfs_getattr }, /* getattr */
{ &vop_setattr_desc, procfs_setattr }, /* setattr */
{ &vop_read_desc, procfs_read }, /* read */
{ &vop_write_desc, procfs_write }, /* write */
{ &vop_ioctl_desc, procfs_ioctl }, /* ioctl */
{ &vop_select_desc, procfs_select }, /* select */
{ &vop_mmap_desc, procfs_mmap }, /* mmap */
{ &vop_revoke_desc, procfs_revoke }, /* revoke */
{ &vop_fsync_desc, procfs_fsync }, /* fsync */
{ &vop_seek_desc, procfs_seek }, /* seek */
{ &vop_remove_desc, procfs_remove }, /* remove */
{ &vop_link_desc, procfs_link }, /* link */
{ &vop_rename_desc, procfs_rename }, /* rename */
{ &vop_mkdir_desc, procfs_mkdir }, /* mkdir */
{ &vop_rmdir_desc, procfs_rmdir }, /* rmdir */
{ &vop_symlink_desc, procfs_symlink }, /* symlink */
{ &vop_readdir_desc, procfs_readdir }, /* readdir */
{ &vop_readlink_desc, procfs_readlink }, /* readlink */
{ &vop_abortop_desc, procfs_abortop }, /* abortop */
{ &vop_inactive_desc, procfs_inactive }, /* inactive */
{ &vop_reclaim_desc, procfs_reclaim }, /* reclaim */
{ &vop_lock_desc, procfs_lock }, /* lock */
{ &vop_unlock_desc, procfs_unlock }, /* unlock */
{ &vop_bmap_desc, procfs_bmap }, /* bmap */
{ &vop_strategy_desc, procfs_strategy }, /* strategy */
{ &vop_print_desc, procfs_print }, /* print */
{ &vop_islocked_desc, procfs_islocked }, /* islocked */
{ &vop_pathconf_desc, procfs_pathconf }, /* pathconf */
{ &vop_advlock_desc, procfs_advlock }, /* advlock */
{ &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */
{ &vop_valloc_desc, procfs_valloc }, /* valloc */
{ &vop_vfree_desc, procfs_vfree }, /* vfree */
{ &vop_truncate_desc, procfs_truncate }, /* truncate */
{ &vop_update_desc, procfs_update }, /* update */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc procfs_vnodeop_opv_desc =
{ &procfs_vnodeop_p, procfs_vnodeop_entries };

View file

@ -0,0 +1,686 @@
/*
* Copyright (c) 1989, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95
*/
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/disklabel.h>
#include <miscfs/specfs/specdev.h>
/* symbolic sleep message strings for devices */
char devopn[] = "devopn";
char devio[] = "devio";
char devwait[] = "devwait";
char devin[] = "devin";
char devout[] = "devout";
char devioc[] = "devioc";
char devcls[] = "devcls";
int (**spec_vnodeop_p)();
struct vnodeopv_entry_desc spec_vnodeop_entries[] = {
{ &vop_default_desc, vn_default_error },
{ &vop_lookup_desc, spec_lookup }, /* lookup */
{ &vop_create_desc, spec_create }, /* create */
{ &vop_mknod_desc, spec_mknod }, /* mknod */
{ &vop_open_desc, spec_open }, /* open */
{ &vop_close_desc, spec_close }, /* close */
{ &vop_access_desc, spec_access }, /* access */
{ &vop_getattr_desc, spec_getattr }, /* getattr */
{ &vop_setattr_desc, spec_setattr }, /* setattr */
{ &vop_read_desc, spec_read }, /* read */
{ &vop_write_desc, spec_write }, /* write */
{ &vop_lease_desc, spec_lease_check }, /* lease */
{ &vop_ioctl_desc, spec_ioctl }, /* ioctl */
{ &vop_select_desc, spec_select }, /* select */
{ &vop_revoke_desc, spec_revoke }, /* revoke */
{ &vop_mmap_desc, spec_mmap }, /* mmap */
{ &vop_fsync_desc, spec_fsync }, /* fsync */
{ &vop_seek_desc, spec_seek }, /* seek */
{ &vop_remove_desc, spec_remove }, /* remove */
{ &vop_link_desc, spec_link }, /* link */
{ &vop_rename_desc, spec_rename }, /* rename */
{ &vop_mkdir_desc, spec_mkdir }, /* mkdir */
{ &vop_rmdir_desc, spec_rmdir }, /* rmdir */
{ &vop_symlink_desc, spec_symlink }, /* symlink */
{ &vop_readdir_desc, spec_readdir }, /* readdir */
{ &vop_readlink_desc, spec_readlink }, /* readlink */
{ &vop_abortop_desc, spec_abortop }, /* abortop */
{ &vop_inactive_desc, spec_inactive }, /* inactive */
{ &vop_reclaim_desc, spec_reclaim }, /* reclaim */
{ &vop_lock_desc, spec_lock }, /* lock */
{ &vop_unlock_desc, spec_unlock }, /* unlock */
{ &vop_bmap_desc, spec_bmap }, /* bmap */
{ &vop_strategy_desc, spec_strategy }, /* strategy */
{ &vop_print_desc, spec_print }, /* print */
{ &vop_islocked_desc, spec_islocked }, /* islocked */
{ &vop_pathconf_desc, spec_pathconf }, /* pathconf */
{ &vop_advlock_desc, spec_advlock }, /* advlock */
{ &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
{ &vop_valloc_desc, spec_valloc }, /* valloc */
{ &vop_vfree_desc, spec_vfree }, /* vfree */
{ &vop_truncate_desc, spec_truncate }, /* truncate */
{ &vop_update_desc, spec_update }, /* update */
{ &vop_bwrite_desc, spec_bwrite }, /* bwrite */
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc spec_vnodeop_opv_desc =
{ &spec_vnodeop_p, spec_vnodeop_entries };
/*
* Trivial lookup routine that always fails.
*/
int
spec_lookup(ap)
struct vop_lookup_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
} */ *ap;
{
*ap->a_vpp = NULL;
return (ENOTDIR);
}
/*
* Open a special file.
*/
/* ARGSUSED */
spec_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct proc *p = ap->a_p;
struct vnode *bvp, *vp = ap->a_vp;
dev_t bdev, dev = (dev_t)vp->v_rdev;
int maj = major(dev);
int error;
/*
* Don't allow open if fs is mounted -nodev.
*/
if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
return (ENXIO);
switch (vp->v_type) {
case VCHR:
if ((u_int)maj >= nchrdev)
return (ENXIO);
if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) {
/*
* When running in very secure mode, do not allow
* opens for writing of any disk character devices.
*/
if (securelevel >= 2 && cdevsw[maj].d_type == D_DISK)
return (EPERM);
/*
* When running in secure mode, do not allow opens
* for writing of /dev/mem, /dev/kmem, or character
* devices whose corresponding block devices are
* currently mounted.
*/
if (securelevel >= 1) {
if ((bdev = chrtoblk(dev)) != NODEV &&
vfinddev(bdev, VBLK, &bvp) &&
bvp->v_usecount > 0 &&
(error = vfs_mountedon(bvp)))
return (error);
if (iskmemdev(dev))
return (EPERM);
}
}
if (cdevsw[maj].d_type == D_TTY)
vp->v_flag |= VISTTY;
VOP_UNLOCK(vp, 0, p);
error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, p);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
return (error);
case VBLK:
if ((u_int)maj >= nblkdev)
return (ENXIO);
/*
* When running in very secure mode, do not allow
* opens for writing of any disk block devices.
*/
if (securelevel >= 2 && ap->a_cred != FSCRED &&
(ap->a_mode & FWRITE) && bdevsw[maj].d_type == D_DISK)
return (EPERM);
/*
* Do not allow opens of block devices that are
* currently mounted.
*/
if (error = vfs_mountedon(vp))
return (error);
return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, p));
}
return (0);
}
/*
* Vnode op for read
*/
/* ARGSUSED */
spec_read(ap)
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct uio *uio = ap->a_uio;
struct proc *p = uio->uio_procp;
struct buf *bp;
daddr_t bn, nextbn;
long bsize, bscale;
struct partinfo dpart;
int n, on, majordev, (*ioctl)();
int error = 0;
dev_t dev;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_READ)
panic("spec_read mode");
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
panic("spec_read proc");
#endif
if (uio->uio_resid == 0)
return (0);
switch (vp->v_type) {
case VCHR:
VOP_UNLOCK(vp, 0, p);
error = (*cdevsw[major(vp->v_rdev)].d_read)
(vp->v_rdev, uio, ap->a_ioflag);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
return (error);
case VBLK:
if (uio->uio_offset < 0)
return (EINVAL);
bsize = BLKDEV_IOSIZE;
dev = vp->v_rdev;
if ((majordev = major(dev)) < nblkdev &&
(ioctl = bdevsw[majordev].d_ioctl) != NULL &&
(*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 &&
dpart.part->p_fstype == FS_BSDFFS &&
dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
bsize = dpart.part->p_frag * dpart.part->p_fsize;
bscale = bsize / DEV_BSIZE;
do {
bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1);
on = uio->uio_offset % bsize;
n = min((unsigned)(bsize - on), uio->uio_resid);
if (vp->v_lastr + bscale == bn) {
nextbn = bn + bscale;
error = breadn(vp, bn, (int)bsize, &nextbn,
(int *)&bsize, 1, NOCRED, &bp);
} else
error = bread(vp, bn, (int)bsize, NOCRED, &bp);
vp->v_lastr = bn;
n = min(n, bsize - bp->b_resid);
if (error) {
brelse(bp);
return (error);
}
error = uiomove((char *)bp->b_data + on, n, uio);
if (n + on == bsize)
bp->b_flags |= B_AGE;
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return (error);
default:
panic("spec_read type");
}
/* NOTREACHED */
}
/*
* Vnode op for write
*/
/* ARGSUSED */
spec_write(ap)
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct uio *uio = ap->a_uio;
struct proc *p = uio->uio_procp;
struct buf *bp;
daddr_t bn;
int bsize, blkmask;
struct partinfo dpart;
register int n, on;
int error = 0;
#ifdef DIAGNOSTIC
if (uio->uio_rw != UIO_WRITE)
panic("spec_write mode");
if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
panic("spec_write proc");
#endif
switch (vp->v_type) {
case VCHR:
VOP_UNLOCK(vp, 0, p);
error = (*cdevsw[major(vp->v_rdev)].d_write)
(vp->v_rdev, uio, ap->a_ioflag);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
return (error);
case VBLK:
if (uio->uio_resid == 0)
return (0);
if (uio->uio_offset < 0)
return (EINVAL);
bsize = BLKDEV_IOSIZE;
if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART,
(caddr_t)&dpart, FREAD, p) == 0) {
if (dpart.part->p_fstype == FS_BSDFFS &&
dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
bsize = dpart.part->p_frag *
dpart.part->p_fsize;
}
blkmask = (bsize / DEV_BSIZE) - 1;
do {
bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask;
on = uio->uio_offset % bsize;
n = min((unsigned)(bsize - on), uio->uio_resid);
if (n == bsize)
bp = getblk(vp, bn, bsize, 0, 0);
else
error = bread(vp, bn, bsize, NOCRED, &bp);
n = min(n, bsize - bp->b_resid);
if (error) {
brelse(bp);
return (error);
}
error = uiomove((char *)bp->b_data + on, n, uio);
if (n + on == bsize) {
bp->b_flags |= B_AGE;
bawrite(bp);
} else
bdwrite(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
return (error);
default:
panic("spec_write type");
}
/* NOTREACHED */
}
/*
* Device ioctl operation.
*/
/* ARGSUSED */
spec_ioctl(ap)
struct vop_ioctl_args /* {
struct vnode *a_vp;
int a_command;
caddr_t a_data;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
dev_t dev = ap->a_vp->v_rdev;
switch (ap->a_vp->v_type) {
case VCHR:
return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
ap->a_fflag, ap->a_p));
case VBLK:
if (ap->a_command == 0 && (int)ap->a_data == B_TAPE)
if (bdevsw[major(dev)].d_type == D_TAPE)
return (0);
else
return (1);
return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
ap->a_fflag, ap->a_p));
default:
panic("spec_ioctl");
/* NOTREACHED */
}
}
/* ARGSUSED */
spec_select(ap)
struct vop_select_args /* {
struct vnode *a_vp;
int a_which;
int a_fflags;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register dev_t dev;
switch (ap->a_vp->v_type) {
default:
return (1); /* XXX */
case VCHR:
dev = ap->a_vp->v_rdev;
return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p);
}
}
/*
* Synch buffers associated with a block device
*/
/* ARGSUSED */
int
spec_fsync(ap)
struct vop_fsync_args /* {
struct vnode *a_vp;
struct ucred *a_cred;
int a_waitfor;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct buf *bp;
struct buf *nbp;
int s;
if (vp->v_type == VCHR)
return (0);
/*
* Flush all dirty buffers associated with a block device.
*/
loop:
s = splbio();
for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
nbp = bp->b_vnbufs.le_next;
if ((bp->b_flags & B_BUSY))
continue;
if ((bp->b_flags & B_DELWRI) == 0)
panic("spec_fsync: not dirty");
bremfree(bp);
bp->b_flags |= B_BUSY;
splx(s);
bawrite(bp);
goto loop;
}
if (ap->a_waitfor == MNT_WAIT) {
while (vp->v_numoutput) {
vp->v_flag |= VBWAIT;
sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
}
#ifdef DIAGNOSTIC
if (vp->v_dirtyblkhd.lh_first) {
vprint("spec_fsync: dirty", vp);
goto loop;
}
#endif
}
splx(s);
return (0);
}
int
spec_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return (0);
}
/*
* Just call the device strategy routine
*/
spec_strategy(ap)
struct vop_strategy_args /* {
struct buf *a_bp;
} */ *ap;
{
(*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
return (0);
}
/*
* This is a noop, simply returning what one has been given.
*/
spec_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct vnode **a_vpp;
daddr_t *a_bnp;
int *a_runp;
} */ *ap;
{
if (ap->a_vpp != NULL)
*ap->a_vpp = ap->a_vp;
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
return (0);
}
/*
* Device close routine
*/
/* ARGSUSED */
spec_close(ap)
struct vop_close_args /* {
struct vnode *a_vp;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
dev_t dev = vp->v_rdev;
int (*devclose) __P((dev_t, int, int, struct proc *));
int mode, error;
switch (vp->v_type) {
case VCHR:
/*
* Hack: a tty device that is a controlling terminal
* has a reference from the session structure.
* We cannot easily tell that a character device is
* a controlling terminal, unless it is the closing
* process' controlling terminal. In that case,
* if the reference count is 2 (this last descriptor
* plus the session), release the reference from the session.
*/
if (vcount(vp) == 2 && ap->a_p &&
vp == ap->a_p->p_session->s_ttyvp) {
vrele(vp);
ap->a_p->p_session->s_ttyvp = NULL;
}
/*
* If the vnode is locked, then we are in the midst
* of forcably closing the device, otherwise we only
* close on last reference.
*/
if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
return (0);
devclose = cdevsw[major(dev)].d_close;
mode = S_IFCHR;
break;
case VBLK:
/*
* On last close of a block device (that isn't mounted)
* we must invalidate any in core blocks, so that
* we can, for instance, change floppy disks.
*/
if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0))
return (error);
/*
* We do not want to really close the device if it
* is still in use unless we are trying to close it
* forcibly. Since every use (buffer, vnode, swap, cmap)
* holds a reference to the vnode, and because we mark
* any other vnodes that alias this device, when the
* sum of the reference counts on all the aliased
* vnodes descends to one, we are on last close.
*/
if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
return (0);
devclose = bdevsw[major(dev)].d_close;
mode = S_IFBLK;
break;
default:
panic("spec_close: not special");
}
return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p));
}
/*
* Print out the contents of a special device vnode.
*/
spec_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev),
minor(ap->a_vp->v_rdev));
}
/*
* Return POSIX pathconf information applicable to special devices.
*/
spec_pathconf(ap)
struct vop_pathconf_args /* {
struct vnode *a_vp;
int a_name;
int *a_retval;
} */ *ap;
{
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);
default:
return (EINVAL);
}
/* NOTREACHED */
}
/*
* Special device advisory byte-level locks.
*/
/* ARGSUSED */
spec_advlock(ap)
struct vop_advlock_args /* {
struct vnode *a_vp;
caddr_t a_id;
int a_op;
struct flock *a_fl;
int a_flags;
} */ *ap;
{
return (EOPNOTSUPP);
}
/*
* Special device failed operation
*/
spec_ebadf()
{
return (EBADF);
}
/*
* Special device bad operation
*/
spec_badop()
{
panic("spec_badop called");
/* NOTREACHED */
}

129
sys/miscfs/specfs/specdev.h Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*
* @(#)specdev.h 8.6 (Berkeley) 5/21/95
*/
/*
* This structure defines the information maintained about
* special devices. It is allocated in checkalias and freed
* in vgone.
*/
struct specinfo {
struct vnode **si_hashchain;
struct vnode *si_specnext;
long si_flags;
dev_t si_rdev;
};
/*
* Exported shorthand
*/
#define v_rdev v_specinfo->si_rdev
#define v_hashchain v_specinfo->si_hashchain
#define v_specnext v_specinfo->si_specnext
#define v_specflags v_specinfo->si_flags
/*
* Flags for specinfo
*/
#define SI_MOUNTEDON 0x0001 /* block special device is mounted on */
/*
* Special device management
*/
#define SPECHSZ 64
#if ((SPECHSZ&(SPECHSZ-1)) == 0)
#define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1))
#else
#define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ)
#endif
struct vnode *speclisth[SPECHSZ];
/*
* Prototypes for special file operations on vnodes.
*/
extern int (**spec_vnodeop_p)();
struct nameidata;
struct componentname;
struct ucred;
struct flock;
struct buf;
struct uio;
int spec_badop(),
spec_ebadf();
int spec_lookup __P((struct vop_lookup_args *));
#define spec_create ((int (*) __P((struct vop_create_args *)))spec_badop)
#define spec_mknod ((int (*) __P((struct vop_mknod_args *)))spec_badop)
int spec_open __P((struct vop_open_args *));
int spec_close __P((struct vop_close_args *));
#define spec_access ((int (*) __P((struct vop_access_args *)))spec_ebadf)
#define spec_getattr ((int (*) __P((struct vop_getattr_args *)))spec_ebadf)
#define spec_setattr ((int (*) __P((struct vop_setattr_args *)))spec_ebadf)
int spec_read __P((struct vop_read_args *));
int spec_write __P((struct vop_write_args *));
#define spec_lease_check ((int (*) __P((struct vop_lease_args *)))nullop)
int spec_ioctl __P((struct vop_ioctl_args *));
int spec_select __P((struct vop_select_args *));
#define spec_revoke vop_revoke
#define spec_mmap ((int (*) __P((struct vop_mmap_args *)))spec_badop)
int spec_fsync __P((struct vop_fsync_args *));
#define spec_seek ((int (*) __P((struct vop_seek_args *)))spec_badop)
#define spec_remove ((int (*) __P((struct vop_remove_args *)))spec_badop)
#define spec_link ((int (*) __P((struct vop_link_args *)))spec_badop)
#define spec_rename ((int (*) __P((struct vop_rename_args *)))spec_badop)
#define spec_mkdir ((int (*) __P((struct vop_mkdir_args *)))spec_badop)
#define spec_rmdir ((int (*) __P((struct vop_rmdir_args *)))spec_badop)
#define spec_symlink ((int (*) __P((struct vop_symlink_args *)))spec_badop)
#define spec_readdir ((int (*) __P((struct vop_readdir_args *)))spec_badop)
#define spec_readlink ((int (*) __P((struct vop_readlink_args *)))spec_badop)
#define spec_abortop ((int (*) __P((struct vop_abortop_args *)))spec_badop)
int spec_inactive __P((struct vop_inactive_args *));
#define spec_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
#define spec_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock)
#define spec_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock)
int spec_bmap __P((struct vop_bmap_args *));
int spec_strategy __P((struct vop_strategy_args *));
int spec_print __P((struct vop_print_args *));
#define spec_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked)
int spec_pathconf __P((struct vop_pathconf_args *));
int spec_advlock __P((struct vop_advlock_args *));
#define spec_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))spec_badop)
#define spec_valloc ((int (*) __P((struct vop_valloc_args *)))spec_badop)
#define spec_reallocblks \
((int (*) __P((struct vop_reallocblks_args *)))spec_badop)
#define spec_vfree ((int (*) __P((struct vop_vfree_args *)))spec_badop)
#define spec_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
#define spec_update ((int (*) __P((struct vop_update_args *)))nullop)
#define spec_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)

91
sys/miscfs/umapfs/umap.h Normal file
View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software donated to Berkeley by
* the UCLA Ficus project.
*
* 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.
*
* @(#)umap.h 8.4 (Berkeley) 8/20/94
*
* @(#)null_vnops.c 1.5 (Berkeley) 7/10/92
*/
#define MAPFILEENTRIES 64
#define GMAPFILEENTRIES 16
#define NOBODY 32767
#define NULLGROUP 65534
struct umap_args {
char *target; /* Target of loopback */
int nentries; /* # of entries in user map array */
int gnentries; /* # of entries in group map array */
u_long (*mapdata)[2]; /* pointer to array of user mappings */
u_long (*gmapdata)[2]; /* pointer to array of group mappings */
};
struct umap_mount {
struct mount *umapm_vfs;
struct vnode *umapm_rootvp; /* Reference to root umap_node */
int info_nentries; /* number of uid mappings */
int info_gnentries; /* number of gid mappings */
u_long info_mapdata[MAPFILEENTRIES][2]; /* mapping data for
user mapping in ficus */
u_long info_gmapdata[GMAPFILEENTRIES][2]; /*mapping data for
group mapping in ficus */
};
#ifdef KERNEL
/*
* A cache of vnode references
*/
struct umap_node {
LIST_ENTRY(umap_node) umap_hash; /* Hash list */
struct vnode *umap_lowervp; /* Aliased vnode - VREFed once */
struct vnode *umap_vnode; /* Back pointer to vnode/umap_node */
};
extern int umap_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
extern u_long umap_reverse_findid __P((u_long id, u_long map[][2], int nentries));
extern void umap_mapids __P((struct mount *v_mount, struct ucred *credp));
#define MOUNTTOUMAPMOUNT(mp) ((struct umap_mount *)((mp)->mnt_data))
#define VTOUMAP(vp) ((struct umap_node *)(vp)->v_data)
#define UMAPTOV(xp) ((xp)->umap_vnode)
#ifdef UMAPFS_DIAGNOSTIC
extern struct vnode *umap_checkvp __P((struct vnode *vp, char *fil, int lno));
#define UMAPVPTOLOWERVP(vp) umap_checkvp((vp), __FILE__, __LINE__)
#else
#define UMAPVPTOLOWERVP(vp) (VTOUMAP(vp)->umap_lowervp)
#endif
extern int (**umap_vnodeop_p)();
extern struct vfsops umap_vfsops;
#endif /* KERNEL */

View file

@ -0,0 +1,380 @@
/*
* 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. 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.
*
* @(#)umap_subr.c 8.9 (Berkeley) 5/14/95
*
* From: $Id: lofs_subr.c, v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <miscfs/umapfs/umap.h>
#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
#define NUMAPNODECACHE 16
/*
* Null layer cache:
* Each cache entry holds a reference to the target vnode
* along with a pointer to the alias vnode. When an
* entry is added the target vnode is VREF'd. When the
* alias is removed the target vnode is vrele'd.
*/
#define UMAP_NHASH(vp) \
(&umap_node_hashtbl[(((u_long)vp)>>LOG2_SIZEVNODE) & umap_node_hash])
LIST_HEAD(umap_node_hashhead, umap_node) *umap_node_hashtbl;
u_long umap_node_hash;
/*
* Initialise cache headers
*/
umapfs_init(vfsp)
struct vfsconf *vfsp;
{
#ifdef UMAPFS_DIAGNOSTIC
printf("umapfs_init\n"); /* printed during system boot */
#endif
umap_node_hashtbl = hashinit(NUMAPNODECACHE, M_CACHE, &umap_node_hash);
}
/*
* umap_findid is called by various routines in umap_vnodeops.c to
* find a user or group id in a map.
*/
static u_long
umap_findid(id, map, nentries)
u_long id;
u_long map[][2];
int nentries;
{
int i;
/* Find uid entry in map */
i = 0;
while ((i<nentries) && ((map[i][0]) != id))
i++;
if (i < nentries)
return (map[i][1]);
else
return (-1);
}
/*
* umap_reverse_findid is called by umap_getattr() in umap_vnodeops.c to
* find a user or group id in a map, in reverse.
*/
u_long
umap_reverse_findid(id, map, nentries)
u_long id;
u_long map[][2];
int nentries;
{
int i;
/* Find uid entry in map */
i = 0;
while ((i<nentries) && ((map[i][1]) != id))
i++;
if (i < nentries)
return (map[i][0]);
else
return (-1);
}
/*
* Return alias for target vnode if already exists, else 0.
*/
static struct vnode *
umap_node_find(mp, targetvp)
struct mount *mp;
struct vnode *targetvp;
{
struct proc *p = curproc; /* XXX */
struct umap_node_hashhead *hd;
struct umap_node *a;
struct vnode *vp;
#ifdef UMAPFS_DIAGNOSTIC
printf("umap_node_find(mp = %x, target = %x)\n", mp, targetvp);
#endif
/*
* Find hash base, and then search the (two-way) linked
* list looking for a umap_node structure which is referencing
* the target vnode. If found, the increment the umap_node
* reference count (but NOT the target vnode's VREF counter).
*/
hd = UMAP_NHASH(targetvp);
loop:
for (a = hd->lh_first; a != 0; a = a->umap_hash.le_next) {
if (a->umap_lowervp == targetvp &&
a->umap_vnode->v_mount == mp) {
vp = UMAPTOV(a);
/*
* We need vget for the VXLOCK
* stuff, but we don't want to lock
* the lower node.
*/
if (vget(vp, 0, p)) {
#ifdef UMAPFS_DIAGNOSTIC
printf ("umap_node_find: vget failed.\n");
#endif
goto loop;
}
return (vp);
}
}
#ifdef UMAPFS_DIAGNOSTIC
printf("umap_node_find(%x, %x): NOT found\n", mp, targetvp);
#endif
return (0);
}
/*
* Make a new umap_node node.
* Vp is the alias vnode, lofsvp is the target vnode.
* Maintain a reference to (targetvp).
*/
static int
umap_node_alloc(mp, lowervp, vpp)
struct mount *mp;
struct vnode *lowervp;
struct vnode **vpp;
{
struct umap_node_hashhead *hd;
struct umap_node *xp;
struct vnode *othervp, *vp;
int error;
if (error = getnewvnode(VT_UMAP, mp, umap_vnodeop_p, vpp))
return (error);
vp = *vpp;
MALLOC(xp, struct umap_node *, sizeof(struct umap_node),
M_TEMP, M_WAITOK);
vp->v_type = lowervp->v_type;
xp->umap_vnode = vp;
vp->v_data = xp;
xp->umap_lowervp = lowervp;
/*
* Before we insert our new node onto the hash chains,
* check to see if someone else has beaten us to it.
* (We could have slept in MALLOC.)
*/
if (othervp = umap_node_find(lowervp)) {
FREE(xp, M_TEMP);
vp->v_type = VBAD; /* node is discarded */
vp->v_usecount = 0; /* XXX */
*vpp = othervp;
return (0);
}
VREF(lowervp); /* Extra VREF will be vrele'd in umap_node_create */
hd = UMAP_NHASH(lowervp);
LIST_INSERT_HEAD(hd, xp, umap_hash);
return (0);
}
/*
* Try to find an existing umap_node vnode refering
* to it, otherwise make a new umap_node vnode which
* contains a reference to the target vnode.
*/
int
umap_node_create(mp, targetvp, newvpp)
struct mount *mp;
struct vnode *targetvp;
struct vnode **newvpp;
{
struct vnode *aliasvp;
if (aliasvp = umap_node_find(mp, targetvp)) {
/*
* Take another reference to the alias vnode
*/
#ifdef UMAPFS_DIAGNOSTIC
vprint("umap_node_create: exists", ap->umap_vnode);
#endif
/* VREF(aliasvp); */
} else {
int error;
/*
* Get new vnode.
*/
#ifdef UMAPFS_DIAGNOSTIC
printf("umap_node_create: create new alias vnode\n");
#endif
/*
* Make new vnode reference the umap_node.
*/
if (error = umap_node_alloc(mp, targetvp, &aliasvp))
return (error);
/*
* aliasvp is already VREF'd by getnewvnode()
*/
}
vrele(targetvp);
#ifdef UMAPFS_DIAGNOSTIC
vprint("umap_node_create: alias", aliasvp);
vprint("umap_node_create: target", targetvp);
#endif
*newvpp = aliasvp;
return (0);
}
#ifdef UMAPFS_DIAGNOSTIC
int umap_checkvp_barrier = 1;
struct vnode *
umap_checkvp(vp, fil, lno)
struct vnode *vp;
char *fil;
int lno;
{
struct umap_node *a = VTOUMAP(vp);
#if 0
/*
* Can't do this check because vop_reclaim runs
* with funny vop vector.
*/
if (vp->v_op != umap_vnodeop_p) {
printf ("umap_checkvp: on non-umap-node\n");
while (umap_checkvp_barrier) /*WAIT*/ ;
panic("umap_checkvp");
}
#endif
if (a->umap_lowervp == NULL) {
/* Should never happen */
int i; u_long *p;
printf("vp = %x, ZERO ptr\n", vp);
for (p = (u_long *) a, i = 0; i < 8; i++)
printf(" %x", p[i]);
printf("\n");
/* wait for debugger */
while (umap_checkvp_barrier) /*WAIT*/ ;
panic("umap_checkvp");
}
if (a->umap_lowervp->v_usecount < 1) {
int i; u_long *p;
printf("vp = %x, unref'ed lowervp\n", vp);
for (p = (u_long *) a, i = 0; i < 8; i++)
printf(" %x", p[i]);
printf("\n");
/* wait for debugger */
while (umap_checkvp_barrier) /*WAIT*/ ;
panic ("umap with unref'ed lowervp");
}
#if 0
printf("umap %x/%d -> %x/%d [%s, %d]\n",
a->umap_vnode, a->umap_vnode->v_usecount,
a->umap_lowervp, a->umap_lowervp->v_usecount,
fil, lno);
#endif
return (a->umap_lowervp);
}
#endif
/* umap_mapids maps all of the ids in a credential, both user and group. */
void
umap_mapids(v_mount, credp)
struct mount *v_mount;
struct ucred *credp;
{
int i, unentries, gnentries;
u_long *groupmap, *usermap;
uid_t uid;
gid_t gid;
unentries = MOUNTTOUMAPMOUNT(v_mount)->info_nentries;
usermap = &(MOUNTTOUMAPMOUNT(v_mount)->info_mapdata[0][0]);
gnentries = MOUNTTOUMAPMOUNT(v_mount)->info_gnentries;
groupmap = &(MOUNTTOUMAPMOUNT(v_mount)->info_gmapdata[0][0]);
/* Find uid entry in map */
uid = (uid_t) umap_findid(credp->cr_uid, usermap, unentries);
if (uid != -1)
credp->cr_uid = uid;
else
credp->cr_uid = (uid_t) NOBODY;
#ifdef notdef
/* cr_gid is the same as cr_groups[0] in 4BSD */
/* Find gid entry in map */
gid = (gid_t) umap_findid(credp->cr_gid, groupmap, gnentries);
if (gid != -1)
credp->cr_gid = gid;
else
credp->cr_gid = NULLGROUP;
#endif
/* Now we must map each of the set of groups in the cr_groups
structure. */
i = 0;
while (credp->cr_groups[i] != 0) {
gid = (gid_t) umap_findid(credp->cr_groups[i],
groupmap, gnentries);
if (gid != -1)
credp->cr_groups[i++] = gid;
else
credp->cr_groups[i++] = NULLGROUP;
}
}

View file

@ -0,0 +1,407 @@
/*
* 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
* the UCLA Ficus project.
*
* 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.
*
* @(#)umap_vfsops.c 8.8 (Berkeley) 5/14/95
*
* @(#)null_vfsops.c 1.5 (Berkeley) 7/10/92
*/
/*
* Umap Layer
* (See mount_umap(8) for a description of this layer.)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <miscfs/umapfs/umap.h>
/*
* Mount umap layer
*/
int
umapfs_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
struct umap_args args;
struct vnode *lowerrootvp, *vp;
struct vnode *umapm_rootvp;
struct umap_mount *amp;
u_int size;
int error;
#ifdef UMAPFS_DIAGNOSTIC
printf("umapfs_mount(mp = %x)\n", mp);
#endif
/*
* Update is a no-op
*/
if (mp->mnt_flag & MNT_UPDATE) {
return (EOPNOTSUPP);
/* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/
}
/*
* Get argument
*/
if (error = copyin(data, (caddr_t)&args, sizeof(struct umap_args)))
return (error);
/*
* Find lower node
*/
NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
UIO_USERSPACE, args.target, p);
if (error = namei(ndp))
return (error);
/*
* Sanity check on lower vnode
*/
lowerrootvp = ndp->ni_vp;
#ifdef UMAPFS_DIAGNOSTIC
printf("vp = %x, check for VDIR...\n", lowerrootvp);
#endif
vrele(ndp->ni_dvp);
ndp->ni_dvp = 0;
if (lowerrootvp->v_type != VDIR) {
vput(lowerrootvp);
return (EINVAL);
}
#ifdef UMAPFS_DIAGNOSTIC
printf("mp = %x\n", mp);
#endif
amp = (struct umap_mount *) malloc(sizeof(struct umap_mount),
M_UFSMNT, M_WAITOK); /* XXX */
/*
* Save reference to underlying FS
*/
amp->umapm_vfs = lowerrootvp->v_mount;
/*
* Now copy in the number of entries and maps for umap mapping.
*/
amp->info_nentries = args.nentries;
amp->info_gnentries = args.gnentries;
error = copyin(args.mapdata, (caddr_t)amp->info_mapdata,
2*sizeof(u_long)*args.nentries);
if (error)
return (error);
#ifdef UMAP_DIAGNOSTIC
printf("umap_mount:nentries %d\n",args.nentries);
for (i = 0; i < args.nentries; i++)
printf(" %d maps to %d\n", amp->info_mapdata[i][0],
amp->info_mapdata[i][1]);
#endif
error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata,
2*sizeof(u_long)*args.nentries);
if (error)
return (error);
#ifdef UMAP_DIAGNOSTIC
printf("umap_mount:gnentries %d\n",args.gnentries);
for (i = 0; i < args.gnentries; i++)
printf(" group %d maps to %d\n",
amp->info_gmapdata[i][0],
amp->info_gmapdata[i][1]);
#endif
/*
* Save reference. Each mount also holds
* a reference on the root vnode.
*/
error = umap_node_create(mp, lowerrootvp, &vp);
/*
* Unlock the node (either the lower or the alias)
*/
VOP_UNLOCK(vp, 0, p);
/*
* Make sure the node alias worked
*/
if (error) {
vrele(lowerrootvp);
free(amp, M_UFSMNT); /* XXX */
return (error);
}
/*
* Keep a held reference to the root vnode.
* It is vrele'd in umapfs_unmount.
*/
umapm_rootvp = vp;
umapm_rootvp->v_flag |= VROOT;
amp->umapm_rootvp = umapm_rootvp;
if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
mp->mnt_flag |= MNT_LOCAL;
mp->mnt_data = (qaddr_t) amp;
vfs_getnewfsid(mp);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
(void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
&size);
bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
#ifdef UMAPFS_DIAGNOSTIC
printf("umapfs_mount: lower %s, alias at %s\n",
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
#endif
return (0);
}
/*
* VFS start. Nothing needed here - the start routine
* on the underlying filesystem will have been called
* when that filesystem was mounted.
*/
int
umapfs_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
/* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */
}
/*
* Free reference to umap layer
*/
int
umapfs_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
int error;
int flags = 0;
#ifdef UMAPFS_DIAGNOSTIC
printf("umapfs_unmount(mp = %x)\n", mp);
#endif
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
/*
* Clear out buffer cache. I don't think we
* ever get anything cached at this level at the
* moment, but who knows...
*/
#ifdef notyet
mntflushbuf(mp, 0);
if (mntinvalbuf(mp, 1))
return (EBUSY);
#endif
if (umapm_rootvp->v_usecount > 1)
return (EBUSY);
if (error = vflush(mp, umapm_rootvp, flags))
return (error);
#ifdef UMAPFS_DIAGNOSTIC
vprint("alias root of lower", umapm_rootvp);
#endif
/*
* Release reference on underlying root vnode
*/
vrele(umapm_rootvp);
/*
* And blow it away for future re-use
*/
vgone(umapm_rootvp);
/*
* Finally, throw away the umap_mount structure
*/
free(mp->mnt_data, M_UFSMNT); /* XXX */
mp->mnt_data = 0;
return (0);
}
int
umapfs_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct vnode *vp;
#ifdef UMAPFS_DIAGNOSTIC
printf("umapfs_root(mp = %x, vp = %x->%x)\n", mp,
MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
);
#endif
/*
* Return locked reference to root.
*/
vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
VREF(vp);
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
*vpp = vp;
return (0);
}
int
umapfs_quotactl(mp, cmd, uid, arg, p)
struct mount *mp;
int cmd;
uid_t uid;
caddr_t arg;
struct proc *p;
{
return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p));
}
int
umapfs_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
int error;
struct statfs mstat;
#ifdef UMAPFS_DIAGNOSTIC
printf("umapfs_statfs(mp = %x, vp = %x->%x)\n", mp,
MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
);
#endif
bzero(&mstat, sizeof(mstat));
error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p);
if (error)
return (error);
/* now copy across the "interesting" information and fake the rest */
sbp->f_type = mstat.f_type;
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
sbp->f_blocks = mstat.f_blocks;
sbp->f_bfree = mstat.f_bfree;
sbp->f_bavail = mstat.f_bavail;
sbp->f_files = mstat.f_files;
sbp->f_ffree = mstat.f_ffree;
if (sbp != &mp->mnt_stat) {
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
int
umapfs_sync(mp, waitfor, cred, p)
struct mount *mp;
int waitfor;
struct ucred *cred;
struct proc *p;
{
/*
* XXX - Assumes no data cached at umap layer.
*/
return (0);
}
int
umapfs_vget(mp, ino, vpp)
struct mount *mp;
ino_t ino;
struct vnode **vpp;
{
return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp));
}
int
umapfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
struct mount *mp;
struct fid *fidp;
struct mbuf *nam;
struct vnode **vpp;
int *exflagsp;
struct ucred**credanonp;
{
return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, nam, vpp, exflagsp,credanonp));
}
int
umapfs_vptofh(vp, fhp)
struct vnode *vp;
struct fid *fhp;
{
return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp));
}
int umapfs_init __P((struct vfsconf *));
#define umapfs_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
struct vfsops umap_vfsops = {
umapfs_mount,
umapfs_start,
umapfs_unmount,
umapfs_root,
umapfs_quotactl,
umapfs_statfs,
umapfs_sync,
umapfs_vget,
umapfs_fhtovp,
umapfs_vptofh,
umapfs_init,
umapfs_sysctl,
};

View file

@ -0,0 +1,533 @@
/*
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software donated to Berkeley by
* the UCLA Ficus project.
*
* 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.
*
* @(#)umap_vnops.c 8.6 (Berkeley) 5/22/95
*/
/*
* Umap Layer
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <miscfs/umapfs/umap.h>
int umap_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
/*
* This is the 10-Apr-92 bypass routine.
* See null_vnops.c:null_bypass for more details.
*/
int
umap_bypass(ap)
struct vop_generic_args /* {
struct vnodeop_desc *a_desc;
<other random data follows, presumably>
} */ *ap;
{
extern int (**umap_vnodeop_p)(); /* not extern, really "forward" */
struct ucred **credpp = 0, *credp = 0;
struct ucred *savecredp, *savecompcredp = 0;
struct ucred *compcredp = 0;
struct vnode **this_vp_p;
int error;
struct vnode *old_vps[VDESC_MAX_VPS];
struct vnode *vp1 = 0;
struct vnode **vps_p[VDESC_MAX_VPS];
struct vnode ***vppp;
struct vnodeop_desc *descp = ap->a_desc;
int reles, i;
struct componentname **compnamepp = 0;
if (umap_bug_bypass)
printf ("umap_bypass: %s\n", descp->vdesc_name);
#ifdef SAFETY
/*
* We require at least one vp.
*/
if (descp->vdesc_vp_offsets == NULL ||
descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
panic ("umap_bypass: no vp's in map.\n");
#endif
/*
* Map the vnodes going in.
* Later, we'll invoke the operation based on
* the first mapped vnode's operation vector.
*/
reles = descp->vdesc_flags;
for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
break; /* bail out at end of list */
vps_p[i] = this_vp_p =
VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap);
if (i == 0) {
vp1 = *vps_p[0];
}
/*
* We're not guaranteed that any but the first vnode
* are of our type. Check for and don't map any
* that aren't. (Must map first vp or vclean fails.)
*/
if (i && (*this_vp_p)->v_op != umap_vnodeop_p) {
old_vps[i] = NULL;
} else {
old_vps[i] = *this_vp_p;
*(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p);
if (reles & 1)
VREF(*this_vp_p);
}
}
/*
* Fix the credentials. (That's the purpose of this layer.)
*/
if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
credpp = VOPARG_OFFSETTO(struct ucred**,
descp->vdesc_cred_offset, ap);
/* Save old values */
savecredp = (*credpp);
(*credpp) = crdup(savecredp);
credp = *credpp;
if (umap_bug_bypass && credp->cr_uid != 0)
printf("umap_bypass: user was %d, group %d\n",
credp->cr_uid, credp->cr_gid);
/* Map all ids in the credential structure. */
umap_mapids(vp1->v_mount, credp);
if (umap_bug_bypass && credp->cr_uid != 0)
printf("umap_bypass: user now %d, group %d\n",
credp->cr_uid, credp->cr_gid);
}
/* BSD often keeps a credential in the componentname structure
* for speed. If there is one, it better get mapped, too.
*/
if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
compnamepp = VOPARG_OFFSETTO(struct componentname**,
descp->vdesc_componentname_offset, ap);
compcredp = (*compnamepp)->cn_cred;
savecompcredp = compcredp;
compcredp = (*compnamepp)->cn_cred = crdup(savecompcredp);
if (umap_bug_bypass && compcredp->cr_uid != 0)
printf("umap_bypass: component credit user was %d, group %d\n",
compcredp->cr_uid, compcredp->cr_gid);
/* Map all ids in the credential structure. */
umap_mapids(vp1->v_mount, compcredp);
if (umap_bug_bypass && compcredp->cr_uid != 0)
printf("umap_bypass: component credit user now %d, group %d\n",
compcredp->cr_uid, compcredp->cr_gid);
}
/*
* Call the operation on the lower layer
* with the modified argument structure.
*/
error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
/*
* Maintain the illusion of call-by-value
* by restoring vnodes in the argument structure
* to their original value.
*/
reles = descp->vdesc_flags;
for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
break; /* bail out at end of list */
if (old_vps[i]) {
*(vps_p[i]) = old_vps[i];
if (reles & 1)
vrele(*(vps_p[i]));
};
};
/*
* Map the possible out-going vpp
* (Assumes that the lower layer always returns
* a VREF'ed vpp unless it gets an error.)
*/
if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
!(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
!error) {
if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
goto out;
vppp = VOPARG_OFFSETTO(struct vnode***,
descp->vdesc_vpp_offset, ap);
error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp);
};
out:
/*
* Free duplicate cred structure and restore old one.
*/
if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
if (umap_bug_bypass && credp && credp->cr_uid != 0)
printf("umap_bypass: returning-user was %d\n",
credp->cr_uid);
crfree(credp);
(*credpp) = savecredp;
if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
printf("umap_bypass: returning-user now %d\n\n",
(*credpp)->cr_uid);
}
if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0)
printf("umap_bypass: returning-component-user was %d\n",
compcredp->cr_uid);
crfree(compcredp);
(*compnamepp)->cn_cred = savecompcredp;
if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
printf("umap_bypass: returning-component-user now %d\n",
compcredp->cr_uid);
}
return (error);
}
/*
* We handle getattr to change the fsid.
*/
int
umap_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
short uid, gid;
int error, tmpid, nentries, gnentries;
u_long (*mapdata)[2], (*gmapdata)[2];
struct vnode **vp1p;
struct vnodeop_desc *descp = ap->a_desc;
if (error = umap_bypass(ap))
return (error);
/* Requires that arguments be restored. */
ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
/*
* Umap needs to map the uid and gid returned by a stat
* into the proper values for this site. This involves
* finding the returned uid in the mapping information,
* translating it into the uid on the other end,
* and filling in the proper field in the vattr
* structure pointed to by ap->a_vap. The group
* is easier, since currently all groups will be
* translate to the NULLGROUP.
*/
/* Find entry in map */
uid = ap->a_vap->va_uid;
gid = ap->a_vap->va_gid;
if (umap_bug_bypass)
printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid,
gid);
vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap);
nentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries;
mapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata);
gnentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries;
gmapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata);
/* Reverse map the uid for the vnode. Since it's a reverse
map, we can't use umap_mapids() to do it. */
tmpid = umap_reverse_findid(uid, mapdata, nentries);
if (tmpid != -1) {
ap->a_vap->va_uid = (uid_t) tmpid;
if (umap_bug_bypass)
printf("umap_getattr: original uid = %d\n", uid);
} else
ap->a_vap->va_uid = (uid_t) NOBODY;
/* Reverse map the gid for the vnode. */
tmpid = umap_reverse_findid(gid, gmapdata, gnentries);
if (tmpid != -1) {
ap->a_vap->va_gid = (gid_t) tmpid;
if (umap_bug_bypass)
printf("umap_getattr: original gid = %d\n", gid);
} else
ap->a_vap->va_gid = (gid_t) NULLGROUP;
return (0);
}
/*
* We need to process our own vnode lock and then clear the
* interlock flag as it applies only to our vnode, not the
* vnodes below us on the stack.
*/
int
umap_lock(ap)
struct vop_lock_args /* {
struct vnode *a_vp;
int a_flags;
struct proc *a_p;
} */ *ap;
{
vop_nolock(ap);
if ((ap->a_flags & LK_TYPE_MASK) == LK_DRAIN)
return (0);
ap->a_flags &= ~LK_INTERLOCK;
return (null_bypass(ap));
}
/*
* We need to process our own vnode unlock and then clear the
* interlock flag as it applies only to our vnode, not the
* vnodes below us on the stack.
*/
int
umap_unlock(ap)
struct vop_unlock_args /* {
struct vnode *a_vp;
int a_flags;
struct proc *a_p;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
vop_nounlock(ap);
ap->a_flags &= ~LK_INTERLOCK;
return (null_bypass(ap));
}
int
umap_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
/*
* Do nothing (and _don't_ bypass).
* Wait to vrele lowervp until reclaim,
* so that until then our umap_node is in the
* cache and reusable.
*
*/
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return (0);
}
int
umap_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
struct umap_node *xp = VTOUMAP(vp);
struct vnode *lowervp = xp->umap_lowervp;
/* After this assignment, this node will not be re-used. */
xp->umap_lowervp = NULL;
LIST_REMOVE(xp, umap_hash);
FREE(vp->v_data, M_TEMP);
vp->v_data = NULL;
vrele(lowervp);
return (0);
}
int
umap_strategy(ap)
struct vop_strategy_args /* {
struct buf *a_bp;
} */ *ap;
{
struct buf *bp = ap->a_bp;
int error;
struct vnode *savedvp;
savedvp = bp->b_vp;
bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
error = VOP_STRATEGY(ap->a_bp);
bp->b_vp = savedvp;
return (error);
}
int
umap_bwrite(ap)
struct vop_bwrite_args /* {
struct buf *a_bp;
} */ *ap;
{
struct buf *bp = ap->a_bp;
int error;
struct vnode *savedvp;
savedvp = bp->b_vp;
bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
error = VOP_BWRITE(ap->a_bp);
bp->b_vp = savedvp;
return (error);
}
int
umap_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct vnode *vp = ap->a_vp;
printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp));
return (0);
}
int
umap_rename(ap)
struct vop_rename_args /* {
struct vnode *a_fdvp;
struct vnode *a_fvp;
struct componentname *a_fcnp;
struct vnode *a_tdvp;
struct vnode *a_tvp;
struct componentname *a_tcnp;
} */ *ap;
{
int error;
struct componentname *compnamep;
struct ucred *compcredp, *savecompcredp;
struct vnode *vp;
/*
* Rename is irregular, having two componentname structures.
* We need to map the cre in the second structure,
* and then bypass takes care of the rest.
*/
vp = ap->a_fdvp;
compnamep = ap->a_tcnp;
compcredp = compnamep->cn_cred;
savecompcredp = compcredp;
compcredp = compnamep->cn_cred = crdup(savecompcredp);
if (umap_bug_bypass && compcredp->cr_uid != 0)
printf("umap_rename: rename component credit user was %d, group %d\n",
compcredp->cr_uid, compcredp->cr_gid);
/* Map all ids in the credential structure. */
umap_mapids(vp->v_mount, compcredp);
if (umap_bug_bypass && compcredp->cr_uid != 0)
printf("umap_rename: rename component credit user now %d, group %d\n",
compcredp->cr_uid, compcredp->cr_gid);
error = umap_bypass(ap);
/* Restore the additional mapped componentname cred structure. */
crfree(compcredp);
compnamep->cn_cred = savecompcredp;
return error;
}
/*
* Global vfs data structures
*/
/*
* XXX - strategy, bwrite are hand coded currently. They should
* go away with a merged buffer/block cache.
*
*/
int (**umap_vnodeop_p)();
struct vnodeopv_entry_desc umap_vnodeop_entries[] = {
{ &vop_default_desc, umap_bypass },
{ &vop_getattr_desc, umap_getattr },
{ &vop_lock_desc, umap_lock },
{ &vop_unlock_desc, umap_unlock },
{ &vop_inactive_desc, umap_inactive },
{ &vop_reclaim_desc, umap_reclaim },
{ &vop_print_desc, umap_print },
{ &vop_rename_desc, umap_rename },
{ &vop_strategy_desc, umap_strategy },
{ &vop_bwrite_desc, umap_bwrite },
{ (struct vnodeop_desc*) NULL, (int(*)()) NULL }
};
struct vnodeopv_desc umap_vnodeop_opv_desc =
{ &umap_vnodeop_p, umap_vnodeop_entries };

8
sys/miscfs/union/README Normal file
View file

@ -0,0 +1,8 @@
If you plan on using union mounts, then you should consider replacing
three files in "libc/gen" in the C library with the files in "libc"
in this directory. The replacement version of opendir() automatically
removes duplicate names when a union stack is encountered. The other
two files do special handling of whiteouts. You will then need to
rebuild the C library and all commands.
@(#)README 8.2 (Berkeley) 11/4/94

995
sys/miscfs/union/libc.fts.c Normal file
View file

@ -0,0 +1,995 @@
/*-
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static FTSENT *fts_alloc __P((FTS *, char *, int));
static FTSENT *fts_build __P((FTS *, int));
static void fts_lfree __P((FTSENT *));
static void fts_load __P((FTS *, FTSENT *));
static size_t fts_maxarglen __P((char * const *));
static void fts_padjust __P((FTS *, void *));
static int fts_palloc __P((FTS *, size_t));
static FTSENT *fts_sort __P((FTS *, FTSENT *, int));
static u_short fts_stat __P((FTS *, FTSENT *, int));
#define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
#define ISSET(opt) (sp->fts_options & opt)
#define SET(opt) (sp->fts_options |= opt)
#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))
#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
/* fts_build flags */
#define BCHILD 1 /* fts_children */
#define BNAMES 2 /* fts_children, names only */
#define BREAD 3 /* fts_read */
FTS *
fts_open(argv, options, compar)
char * const *argv;
register int options;
int (*compar)();
{
register FTS *sp;
register FTSENT *p, *root;
register int nitems;
FTSENT *parent, *tmp;
int len;
/* Options check. */
if (options & ~FTS_OPTIONMASK) {
errno = EINVAL;
return (NULL);
}
/* Allocate/initialize the stream */
if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
return (NULL);
memset(sp, 0, sizeof(FTS));
sp->fts_compar = compar;
sp->fts_options = options;
/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
if (ISSET(FTS_LOGICAL))
SET(FTS_NOCHDIR);
/*
* Start out with 1K of path space, and enough, in any case,
* to hold the user's paths.
*/
if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
goto mem1;
/* Allocate/initialize root's parent. */
if ((parent = fts_alloc(sp, "", 0)) == NULL)
goto mem2;
parent->fts_level = FTS_ROOTPARENTLEVEL;
/* Allocate/initialize root(s). */
for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
/* Don't allow zero-length paths. */
if ((len = strlen(*argv)) == 0) {
errno = ENOENT;
goto mem3;
}
p = fts_alloc(sp, *argv, len);
p->fts_level = FTS_ROOTLEVEL;
p->fts_parent = parent;
p->fts_accpath = p->fts_name;
p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
/* Command-line "." and ".." are real directories. */
if (p->fts_info == FTS_DOT)
p->fts_info = FTS_D;
/*
* If comparison routine supplied, traverse in sorted
* order; otherwise traverse in the order specified.
*/
if (compar) {
p->fts_link = root;
root = p;
} else {
p->fts_link = NULL;
if (root == NULL)
tmp = root = p;
else {
tmp->fts_link = p;
tmp = p;
}
}
}
if (compar && nitems > 1)
root = fts_sort(sp, root, nitems);
/*
* Allocate a dummy pointer and make fts_read think that we've just
* finished the node before the root(s); set p->fts_info to FTS_INIT
* so that everything about the "current" node is ignored.
*/
if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
goto mem3;
sp->fts_cur->fts_link = root;
sp->fts_cur->fts_info = FTS_INIT;
/*
* If using chdir(2), grab a file descriptor pointing to dot to insure
* that we can get back here; this could be avoided for some paths,
* but almost certainly not worth the effort. Slashes, symbolic links,
* and ".." are all fairly nasty problems. Note, if we can't get the
* descriptor we run anyway, just more slowly.
*/
if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
SET(FTS_NOCHDIR);
return (sp);
mem3: fts_lfree(root);
free(parent);
mem2: free(sp->fts_path);
mem1: free(sp);
return (NULL);
}
static void
fts_load(sp, p)
FTS *sp;
register FTSENT *p;
{
register int len;
register char *cp;
/*
* Load the stream structure for the next traversal. Since we don't
* actually enter the directory until after the preorder visit, set
* the fts_accpath field specially so the chdir gets done to the right
* place and the user can access the first node. From fts_open it's
* known that the path will fit.
*/
len = p->fts_pathlen = p->fts_namelen;
memmove(sp->fts_path, p->fts_name, len + 1);
if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
len = strlen(++cp);
memmove(p->fts_name, cp, len + 1);
p->fts_namelen = len;
}
p->fts_accpath = p->fts_path = sp->fts_path;
sp->fts_dev = p->fts_dev;
}
int
fts_close(sp)
FTS *sp;
{
register FTSENT *freep, *p;
int saved_errno;
/*
* This still works if we haven't read anything -- the dummy structure
* points to the root list, so we step through to the end of the root
* list which has a valid parent pointer.
*/
if (sp->fts_cur) {
for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
freep = p;
p = p->fts_link ? p->fts_link : p->fts_parent;
free(freep);
}
free(p);
}
/* Free up child linked list, sort array, path buffer. */
if (sp->fts_child)
fts_lfree(sp->fts_child);
if (sp->fts_array)
free(sp->fts_array);
free(sp->fts_path);
/* Return to original directory, save errno if necessary. */
if (!ISSET(FTS_NOCHDIR)) {
saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
(void)close(sp->fts_rfd);
}
/* Free up the stream pointer. */
free(sp);
/* Set errno and return. */
if (!ISSET(FTS_NOCHDIR) && saved_errno) {
errno = saved_errno;
return (-1);
}
return (0);
}
/*
* Special case a root of "/" so that slashes aren't appended which would
* cause paths to be written as "//foo".
*/
#define NAPPEND(p) \
(p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
FTSENT *
fts_read(sp)
register FTS *sp;
{
register FTSENT *p, *tmp;
register int instr;
register char *t;
int saved_errno;
/* If finished or unrecoverable error, return NULL. */
if (sp->fts_cur == NULL || ISSET(FTS_STOP))
return (NULL);
/* Set current node pointer. */
p = sp->fts_cur;
/* Save and zero out user instructions. */
instr = p->fts_instr;
p->fts_instr = FTS_NOINSTR;
/* Any type of file may be re-visited; re-stat and re-turn. */
if (instr == FTS_AGAIN) {
p->fts_info = fts_stat(sp, p, 0);
return (p);
}
/*
* Following a symlink -- SLNONE test allows application to see
* SLNONE and recover. If indirecting through a symlink, have
* keep a pointer to current location. If unable to get that
* pointer, follow fails.
*/
if (instr == FTS_FOLLOW &&
(p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
p->fts_info = fts_stat(sp, p, 1);
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
p->fts_errno = errno;
p->fts_info = FTS_ERR;
} else
p->fts_flags |= FTS_SYMFOLLOW;
return (p);
}
/* Directory in pre-order. */
if (p->fts_info == FTS_D) {
/* If skipped or crossed mount point, do post-order visit. */
if (instr == FTS_SKIP ||
ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) {
if (p->fts_flags & FTS_SYMFOLLOW)
(void)close(p->fts_symfd);
if (sp->fts_child) {
fts_lfree(sp->fts_child);
sp->fts_child = NULL;
}
p->fts_info = FTS_DP;
return (p);
}
/* Rebuild if only read the names and now traversing. */
if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) {
sp->fts_options &= ~FTS_NAMEONLY;
fts_lfree(sp->fts_child);
sp->fts_child = NULL;
}
/*
* Cd to the subdirectory.
*
* If have already read and now fail to chdir, whack the list
* to make the names come out right, and set the parent errno
* so the application will eventually get an error condition.
* Set the FTS_DONTCHDIR flag so that when we logically change
* directories back to the parent we don't do a chdir.
*
* If haven't read do so. If the read fails, fts_build sets
* FTS_STOP or the fts_info field of the node.
*/
if (sp->fts_child) {
if (CHDIR(sp, p->fts_accpath)) {
p->fts_errno = errno;
p->fts_flags |= FTS_DONTCHDIR;
for (p = sp->fts_child; p; p = p->fts_link)
p->fts_accpath =
p->fts_parent->fts_accpath;
}
} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
if (ISSET(FTS_STOP))
return (NULL);
return (p);
}
p = sp->fts_child;
sp->fts_child = NULL;
goto name;
}
/* Move to the next node on this level. */
next: tmp = p;
if (p = p->fts_link) {
free(tmp);
/*
* If reached the top, return to the original directory, and
* load the paths for the next root.
*/
if (p->fts_level == FTS_ROOTLEVEL) {
if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
SET(FTS_STOP);
return (NULL);
}
fts_load(sp, p);
return (sp->fts_cur = p);
}
/*
* User may have called fts_set on the node. If skipped,
* ignore. If followed, get a file descriptor so we can
* get back if necessary.
*/
if (p->fts_instr == FTS_SKIP)
goto next;
if (p->fts_instr == FTS_FOLLOW) {
p->fts_info = fts_stat(sp, p, 1);
if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
if ((p->fts_symfd =
open(".", O_RDONLY, 0)) < 0) {
p->fts_errno = errno;
p->fts_info = FTS_ERR;
} else
p->fts_flags |= FTS_SYMFOLLOW;
p->fts_instr = FTS_NOINSTR;
}
name: t = sp->fts_path + NAPPEND(p->fts_parent);
*t++ = '/';
memmove(t, p->fts_name, p->fts_namelen + 1);
return (sp->fts_cur = p);
}
/* Move up to the parent node. */
p = tmp->fts_parent;
free(tmp);
if (p->fts_level == FTS_ROOTPARENTLEVEL) {
/*
* Done; free everything up and set errno to 0 so the user
* can distinguish between error and EOF.
*/
free(p);
errno = 0;
return (sp->fts_cur = NULL);
}
/* Nul terminate the pathname. */
sp->fts_path[p->fts_pathlen] = '\0';
/*
* Return to the parent directory. If at a root node or came through
* a symlink, go back through the file descriptor. Otherwise, cd up
* one directory.
*/
if (p->fts_level == FTS_ROOTLEVEL) {
if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
SET(FTS_STOP);
return (NULL);
}
} else if (p->fts_flags & FTS_SYMFOLLOW) {
if (FCHDIR(sp, p->fts_symfd)) {
saved_errno = errno;
(void)close(p->fts_symfd);
errno = saved_errno;
SET(FTS_STOP);
return (NULL);
}
(void)close(p->fts_symfd);
} else if (!(p->fts_flags & FTS_DONTCHDIR)) {
if (CHDIR(sp, "..")) {
SET(FTS_STOP);
return (NULL);
}
}
p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
return (sp->fts_cur = p);
}
/*
* Fts_set takes the stream as an argument although it's not used in this
* implementation; it would be necessary if anyone wanted to add global
* semantics to fts using fts_set. An error return is allowed for similar
* reasons.
*/
/* ARGSUSED */
int
fts_set(sp, p, instr)
FTS *sp;
FTSENT *p;
int instr;
{
if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
instr != FTS_NOINSTR && instr != FTS_SKIP) {
errno = EINVAL;
return (1);
}
p->fts_instr = instr;
return (0);
}
FTSENT *
fts_children(sp, instr)
register FTS *sp;
int instr;
{
register FTSENT *p;
int fd;
if (instr && instr != FTS_NAMEONLY) {
errno = EINVAL;
return (NULL);
}
/* Set current node pointer. */
p = sp->fts_cur;
/*
* Errno set to 0 so user can distinguish empty directory from
* an error.
*/
errno = 0;
/* Fatal errors stop here. */
if (ISSET(FTS_STOP))
return (NULL);
/* Return logical hierarchy of user's arguments. */
if (p->fts_info == FTS_INIT)
return (p->fts_link);
/*
* If not a directory being visited in pre-order, stop here. Could
* allow FTS_DNR, assuming the user has fixed the problem, but the
* same effect is available with FTS_AGAIN.
*/
if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
return (NULL);
/* Free up any previous child list. */
if (sp->fts_child)
fts_lfree(sp->fts_child);
if (instr == FTS_NAMEONLY) {
sp->fts_options |= FTS_NAMEONLY;
instr = BNAMES;
} else
instr = BCHILD;
/*
* If using chdir on a relative path and called BEFORE fts_read does
* its chdir to the root of a traversal, we can lose -- we need to
* chdir into the subdirectory, and we don't know where the current
* directory is, so we can't get back so that the upcoming chdir by
* fts_read will work.
*/
if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
ISSET(FTS_NOCHDIR))
return (sp->fts_child = fts_build(sp, instr));
if ((fd = open(".", O_RDONLY, 0)) < 0)
return (NULL);
sp->fts_child = fts_build(sp, instr);
if (fchdir(fd))
return (NULL);
(void)close(fd);
return (sp->fts_child);
}
/*
* This is the tricky part -- do not casually change *anything* in here. The
* idea is to build the linked list of entries that are used by fts_children
* and fts_read. There are lots of special cases.
*
* The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
* set and it's a physical walk (so that symbolic links can't be directories),
* we can do things quickly. First, if it's a 4.4BSD file system, the type
* of the file is in the directory entry. Otherwise, we assume that the number
* of subdirectories in a node is equal to the number of links to the parent.
* The former skips all stat calls. The latter skips stat calls in any leaf
* directories and for any files after the subdirectories in the directory have
* been found, cutting the stat calls by about 2/3.
*/
static FTSENT *
fts_build(sp, type)
register FTS *sp;
int type;
{
register struct dirent *dp;
register FTSENT *p, *head;
register int nitems;
FTSENT *cur, *tail;
DIR *dirp;
void *adjaddr;
int cderrno, descend, len, level, maxlen, nlinks, oflag, saved_errno;
char *cp;
/* Set current node pointer. */
cur = sp->fts_cur;
/*
* Open the directory for reading. If this fails, we're done.
* If being called from fts_read, set the fts_info field.
*/
#ifdef FTS_WHITEOUT
if (ISSET(FTS_WHITEOUT))
oflag = DTF_NODUP|DTF_REWIND;
else
oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
#else
#define __opendir2(path, flag) opendir(path)
#endif
if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
if (type == BREAD) {
cur->fts_info = FTS_DNR;
cur->fts_errno = errno;
}
return (NULL);
}
/*
* Nlinks is the number of possible entries of type directory in the
* directory if we're cheating on stat calls, 0 if we're not doing
* any stat calls at all, -1 if we're doing stats on everything.
*/
if (type == BNAMES)
nlinks = 0;
else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL))
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
else
nlinks = -1;
#ifdef notdef
(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
#endif
/*
* If we're going to need to stat anything or we want to descend
* and stay in the directory, chdir. If this fails we keep going,
* but set a flag so we don't chdir after the post-order visit.
* We won't be able to stat anything, but we can still return the
* names themselves. Note, that since fts_read won't be able to
* chdir into the directory, it will have to return different path
* names than before, i.e. "a/b" instead of "b". Since the node
* has already been visited in pre-order, have to wait until the
* post-order visit to return the error. There is a special case
* here, if there was nothing to stat then it's not an error to
* not be able to stat. This is all fairly nasty. If a program
* needed sorted entries or stat information, they had better be
* checking FTS_NS on the returned nodes.
*/
cderrno = 0;
if (nlinks || type == BREAD)
if (FCHDIR(sp, dirfd(dirp))) {
if (nlinks && type == BREAD)
cur->fts_errno = errno;
cur->fts_flags |= FTS_DONTCHDIR;
descend = 0;
cderrno = errno;
} else
descend = 1;
else
descend = 0;
/*
* Figure out the max file name length that can be stored in the
* current path -- the inner loop allocates more path as necessary.
* We really wouldn't have to do the maxlen calculations here, we
* could do them in fts_read before returning the path, but it's a
* lot easier here since the length is part of the dirent structure.
*
* If not changing directories set a pointer so that can just append
* each new name into the path.
*/
maxlen = sp->fts_pathlen - cur->fts_pathlen - 1;
len = NAPPEND(cur);
if (ISSET(FTS_NOCHDIR)) {
cp = sp->fts_path + len;
*cp++ = '/';
}
level = cur->fts_level + 1;
/* Read the directory, attaching each entry to the `link' pointer. */
adjaddr = NULL;
for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) {
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
continue;
if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL)
goto mem1;
if (dp->d_namlen > maxlen) {
if (fts_palloc(sp, (size_t)dp->d_namlen)) {
/*
* No more memory for path or structures. Save
* errno, free up the current structure and the
* structures already allocated.
*/
mem1: saved_errno = errno;
if (p)
free(p);
fts_lfree(head);
(void)closedir(dirp);
errno = saved_errno;
cur->fts_info = FTS_ERR;
SET(FTS_STOP);
return (NULL);
}
adjaddr = sp->fts_path;
maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
}
p->fts_pathlen = len + dp->d_namlen + 1;
p->fts_parent = sp->fts_cur;
p->fts_level = level;
#ifdef FTS_WHITEOUT
if (dp->d_type == DT_WHT)
p->fts_flags |= FTS_ISW;
#endif
if (cderrno) {
if (nlinks) {
p->fts_info = FTS_NS;
p->fts_errno = cderrno;
} else
p->fts_info = FTS_NSOK;
p->fts_accpath = cur->fts_accpath;
} else if (nlinks == 0
#ifdef DT_DIR
|| nlinks > 0 &&
dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN
#endif
) {
p->fts_accpath =
ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
p->fts_info = FTS_NSOK;
} else {
/* Build a file name for fts_stat to stat. */
if (ISSET(FTS_NOCHDIR)) {
p->fts_accpath = p->fts_path;
memmove(cp, p->fts_name, p->fts_namelen + 1);
} else
p->fts_accpath = p->fts_name;
/* Stat it. */
p->fts_info = fts_stat(sp, p, 0);
/* Decrement link count if applicable. */
if (nlinks > 0 && (p->fts_info == FTS_D ||
p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
--nlinks;
}
/* We walk in directory order so "ls -f" doesn't get upset. */
p->fts_link = NULL;
if (head == NULL)
head = tail = p;
else {
tail->fts_link = p;
tail = p;
}
++nitems;
}
(void)closedir(dirp);
/*
* If had to realloc the path, adjust the addresses for the rest
* of the tree.
*/
if (adjaddr)
fts_padjust(sp, adjaddr);
/*
* If not changing directories, reset the path back to original
* state.
*/
if (ISSET(FTS_NOCHDIR)) {
if (cp - 1 > sp->fts_path)
--cp;
*cp = '\0';
}
/*
* If descended after called from fts_children or after called from
* fts_read and nothing found, get back. At the root level we use
* the saved fd; if one of fts_open()'s arguments is a relative path
* to an empty directory, we wind up here with no other way back. If
* can't get back, we're done.
*/
if (descend && (type == BCHILD || !nitems) &&
(cur->fts_level == FTS_ROOTLEVEL ?
FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) {
cur->fts_info = FTS_ERR;
SET(FTS_STOP);
return (NULL);
}
/* If didn't find anything, return NULL. */
if (!nitems) {
if (type == BREAD)
cur->fts_info = FTS_DP;
return (NULL);
}
/* Sort the entries. */
if (sp->fts_compar && nitems > 1)
head = fts_sort(sp, head, nitems);
return (head);
}
static u_short
fts_stat(sp, p, follow)
FTS *sp;
register FTSENT *p;
int follow;
{
register FTSENT *t;
register dev_t dev;
register ino_t ino;
struct stat *sbp, sb;
int saved_errno;
/* If user needs stat info, stat buffer already allocated. */
sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
#ifdef FTS_WHITEOUT
/* check for whiteout */
if (p->fts_flags & FTS_ISW) {
if (sbp != &sb) {
memset(sbp, '\0', sizeof (*sbp));
sbp->st_mode = S_IFWHT;
}
return (FTS_W);
}
#endif
/*
* If doing a logical walk, or application requested FTS_FOLLOW, do
* a stat(2). If that fails, check for a non-existent symlink. If
* fail, set the errno from the stat call.
*/
if (ISSET(FTS_LOGICAL) || follow) {
if (stat(p->fts_accpath, sbp)) {
saved_errno = errno;
if (!lstat(p->fts_accpath, sbp)) {
errno = 0;
return (FTS_SLNONE);
}
p->fts_errno = saved_errno;
goto err;
}
} else if (lstat(p->fts_accpath, sbp)) {
p->fts_errno = errno;
err: memset(sbp, 0, sizeof(struct stat));
return (FTS_NS);
}
if (S_ISDIR(sbp->st_mode)) {
/*
* Set the device/inode. Used to find cycles and check for
* crossing mount points. Also remember the link count, used
* in fts_build to limit the number of stat calls. It is
* understood that these fields are only referenced if fts_info
* is set to FTS_D.
*/
dev = p->fts_dev = sbp->st_dev;
ino = p->fts_ino = sbp->st_ino;
p->fts_nlink = sbp->st_nlink;
if (ISDOT(p->fts_name))
return (FTS_DOT);
/*
* Cycle detection is done by brute force when the directory
* is first encountered. If the tree gets deep enough or the
* number of symbolic links to directories is high enough,
* something faster might be worthwhile.
*/
for (t = p->fts_parent;
t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
if (ino == t->fts_ino && dev == t->fts_dev) {
p->fts_cycle = t;
return (FTS_DC);
}
return (FTS_D);
}
if (S_ISLNK(sbp->st_mode))
return (FTS_SL);
if (S_ISREG(sbp->st_mode))
return (FTS_F);
return (FTS_DEFAULT);
}
static FTSENT *
fts_sort(sp, head, nitems)
FTS *sp;
FTSENT *head;
register int nitems;
{
register FTSENT **ap, *p;
/*
* Construct an array of pointers to the structures and call qsort(3).
* Reassemble the array in the order returned by qsort. If unable to
* sort for memory reasons, return the directory entries in their
* current order. Allocate enough space for the current needs plus
* 40 so don't realloc one entry at a time.
*/
if (nitems > sp->fts_nitems) {
sp->fts_nitems = nitems + 40;
if ((sp->fts_array = realloc(sp->fts_array,
(size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) {
sp->fts_nitems = 0;
return (head);
}
}
for (ap = sp->fts_array, p = head; p; p = p->fts_link)
*ap++ = p;
qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
for (head = *(ap = sp->fts_array); --nitems; ++ap)
ap[0]->fts_link = ap[1];
ap[0]->fts_link = NULL;
return (head);
}
static FTSENT *
fts_alloc(sp, name, namelen)
FTS *sp;
char *name;
register int namelen;
{
register FTSENT *p;
size_t len;
/*
* The file name is a variable length array and no stat structure is
* necessary if the user has set the nostat bit. Allocate the FTSENT
* structure, the file name and the stat structure in one chunk, but
* be careful that the stat structure is reasonably aligned. Since the
* fts_name field is declared to be of size 1, the fts_name pointer is
* namelen + 2 before the first possible address of the stat structure.
*/
len = sizeof(FTSENT) + namelen;
if (!ISSET(FTS_NOSTAT))
len += sizeof(struct stat) + ALIGNBYTES;
if ((p = malloc(len)) == NULL)
return (NULL);
/* Copy the name plus the trailing NULL. */
memmove(p->fts_name, name, namelen + 1);
if (!ISSET(FTS_NOSTAT))
p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
p->fts_namelen = namelen;
p->fts_path = sp->fts_path;
p->fts_errno = 0;
p->fts_flags = 0;
p->fts_instr = FTS_NOINSTR;
p->fts_number = 0;
p->fts_pointer = NULL;
return (p);
}
static void
fts_lfree(head)
register FTSENT *head;
{
register FTSENT *p;
/* Free a linked list of structures. */
while (p = head) {
head = head->fts_link;
free(p);
}
}
/*
* Allow essentially unlimited paths; find, rm, ls should all work on any tree.
* Most systems will allow creation of paths much longer than MAXPATHLEN, even
* though the kernel won't resolve them. Add the size (not just what's needed)
* plus 256 bytes so don't realloc the path 2 bytes at a time.
*/
static int
fts_palloc(sp, more)
FTS *sp;
size_t more;
{
sp->fts_pathlen += more + 256;
sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen);
return (sp->fts_path == NULL);
}
/*
* When the path is realloc'd, have to fix all of the pointers in structures
* already returned.
*/
static void
fts_padjust(sp, addr)
FTS *sp;
void *addr;
{
FTSENT *p;
#define ADJUST(p) { \
(p)->fts_accpath = \
(char *)addr + ((p)->fts_accpath - (p)->fts_path); \
(p)->fts_path = addr; \
}
/* Adjust the current set of children. */
for (p = sp->fts_child; p; p = p->fts_link)
ADJUST(p);
/* Adjust the rest of the tree. */
for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
ADJUST(p);
p = p->fts_link ? p->fts_link : p->fts_parent;
}
}
static size_t
fts_maxarglen(argv)
char * const *argv;
{
size_t len, max;
for (max = 0; *argv; ++argv)
if ((len = strlen(*argv)) > max)
max = len;
return (max);
}

View file

@ -0,0 +1,255 @@
/*
* Copyright (c) 1983, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)opendir.c 8.6 (Berkeley) 8/14/94";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
#include <sys/mount.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
/*
* Open a directory.
*/
DIR *
opendir(name)
const char *name;
{
return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
}
DIR *
__opendir2(name, flags)
const char *name;
int flags;
{
DIR *dirp;
int fd;
int incr;
int unionstack;
if ((fd = open(name, O_RDONLY)) == -1)
return (NULL);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
(dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
close(fd);
return (NULL);
}
/*
* If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
* buffer that it cluster boundary aligned.
* Hopefully this can be a big win someday by allowing page
* trades to user space to be done by getdirentries()
*/
if ((CLBYTES % DIRBLKSIZ) == 0)
incr = CLBYTES;
else
incr = DIRBLKSIZ;
/*
* Determine whether this directory is the top of a union stack.
*/
if (flags & DTF_NODUP) {
struct statfs sfb;
if (fstatfs(fd, &sfb) < 0) {
free(dirp);
close(fd);
return (NULL);
}
unionstack = (sfb.f_type == MOUNT_UNION);
} else {
unionstack = 0;
}
if (unionstack) {
int len = 0;
int space = 0;
char *buf = 0;
char *ddptr = 0;
int n;
struct dirent **dpv;
/*
* The strategy here is to read all the directory
* entries into a buffer, sort the buffer, and
* remove duplicate entries by setting the inode
* number to zero.
*/
do {
/*
* Always make at least DIRBLKSIZ bytes
* available to getdirentries
*/
if (space < DIRBLKSIZ) {
space += incr;
len += incr;
buf = realloc(buf, len);
if (buf == NULL) {
free(dirp);
close(fd);
return (NULL);
}
ddptr = buf + (len - space);
}
n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
if (n > 0) {
ddptr += n;
space -= n;
}
} while (n > 0);
flags |= __DTF_READALL;
/*
* Re-open the directory.
* This has the effect of rewinding back to the
* top of the union stack and is needed by
* programs which plan to fchdir to a descriptor
* which has also been read -- see fts.c.
*/
if (flags & DTF_REWIND) {
(void) close(fd);
if ((fd = open(name, O_RDONLY)) == -1) {
free(buf);
free(dirp);
return (NULL);
}
}
/*
* There is now a buffer full of (possibly) duplicate
* names.
*/
dirp->dd_buf = buf;
/*
* Go round this loop twice...
*
* Scan through the buffer, counting entries.
* On the second pass, save pointers to each one.
* Then sort the pointers and remove duplicate names.
*/
for (dpv = 0;;) {
n = 0;
ddptr = buf;
while (ddptr < buf + len) {
struct dirent *dp;
dp = (struct dirent *) ddptr;
if ((int)dp & 03)
break;
if ((dp->d_reclen <= 0) ||
(dp->d_reclen > (buf + len + 1 - ddptr)))
break;
ddptr += dp->d_reclen;
if (dp->d_fileno) {
if (dpv)
dpv[n] = dp;
n++;
}
}
if (dpv) {
struct dirent *xp;
/*
* This sort must be stable.
*/
mergesort(dpv, n, sizeof(*dpv), alphasort);
dpv[n] = NULL;
xp = NULL;
/*
* Scan through the buffer in sort order,
* zapping the inode number of any
* duplicate names.
*/
for (n = 0; dpv[n]; n++) {
struct dirent *dp = dpv[n];
if ((xp == NULL) ||
strcmp(dp->d_name, xp->d_name)) {
xp = dp;
} else {
dp->d_fileno = 0;
}
if (dp->d_type == DT_WHT &&
(flags & DTF_HIDEW))
dp->d_fileno = 0;
}
free(dpv);
break;
} else {
dpv = malloc((n+1) * sizeof(struct dirent *));
if (dpv == NULL)
break;
}
}
dirp->dd_len = len;
dirp->dd_size = ddptr - dirp->dd_buf;
} else {
dirp->dd_len = incr;
dirp->dd_buf = malloc(dirp->dd_len);
if (dirp->dd_buf == NULL) {
free(dirp);
close (fd);
return (NULL);
}
dirp->dd_seek = 0;
flags &= ~DTF_REWIND;
}
dirp->dd_loc = 0;
dirp->dd_fd = fd;
dirp->dd_flags = flags;
/*
* Set up seek point for rewinddir.
*/
dirp->dd_rewind = telldir(dirp);
return (dirp);
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 1983, 1993
* The Regents of the University of California. All rights reserved.
*
* 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)readdir.c 8.3 (Berkeley) 9/29/94";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
#include <dirent.h>
/*
* get next entry in a directory.
*/
struct dirent *
readdir(dirp)
register DIR *dirp;
{
register struct dirent *dp;
for (;;) {
if (dirp->dd_loc >= dirp->dd_size) {
if (dirp->dd_flags & __DTF_READALL)
return (NULL);
dirp->dd_loc = 0;
}
if (dirp->dd_loc == 0 && !(dirp->dd_flags & __DTF_READALL)) {
dirp->dd_size = getdirentries(dirp->dd_fd,
dirp->dd_buf, dirp->dd_len, &dirp->dd_seek);
if (dirp->dd_size <= 0)
return (NULL);
}
dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc);
if ((int)dp & 03) /* bogus pointer check */
return (NULL);
if (dp->d_reclen <= 0 ||
dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc)
return (NULL);
dirp->dd_loc += dp->d_reclen;
if (dp->d_ino == 0)
continue;
if (dp->d_type == DT_WHT && (dirp->dd_flags & DTF_HIDEW))
continue;
return (dp);
}
}

129
sys/miscfs/union/union.h Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 1994 The Regents of the University of California.
* Copyright (c) 1994 Jan-Simon Pendry.
* 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. 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.
*
* @(#)union.h 8.9 (Berkeley) 12/10/94
*/
struct union_args {
char *target; /* Target of loopback */
int mntflags; /* Options on the mount */
};
#define UNMNT_ABOVE 0x0001 /* Target appears below mount point */
#define UNMNT_BELOW 0x0002 /* Target appears below mount point */
#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
#define UNMNT_OPMASK 0x0003
struct union_mount {
struct vnode *um_uppervp;
struct vnode *um_lowervp;
struct ucred *um_cred; /* Credentials of user calling mount */
int um_cmode; /* cmask from mount process */
int um_op; /* Operation mode */
};
#ifdef KERNEL
/*
* DEFDIRMODE is the mode bits used to create a shadow directory.
*/
#define VRWXMODE (VREAD|VWRITE|VEXEC)
#define VRWMODE (VREAD|VWRITE)
#define UN_DIRMODE ((VRWXMODE)|(VRWXMODE>>3)|(VRWXMODE>>6))
#define UN_FILEMODE ((VRWMODE)|(VRWMODE>>3)|(VRWMODE>>6))
/*
* A cache of vnode references
*/
struct union_node {
LIST_ENTRY(union_node) un_cache; /* Hash chain */
struct vnode *un_vnode; /* Back pointer */
struct vnode *un_uppervp; /* overlaying object */
struct vnode *un_lowervp; /* underlying object */
struct vnode *un_dirvp; /* Parent dir of uppervp */
struct vnode *un_pvp; /* Parent vnode */
char *un_path; /* saved component name */
int un_hash; /* saved un_path hash value */
int un_openl; /* # of opens on lowervp */
unsigned int un_flags;
struct vnode **un_dircache; /* cached union stack */
off_t un_uppersz; /* size of upper object */
off_t un_lowersz; /* size of lower object */
#ifdef DIAGNOSTIC
pid_t un_pid;
#endif
};
#define UN_WANT 0x01
#define UN_LOCKED 0x02
#define UN_ULOCK 0x04 /* Upper node is locked */
#define UN_KLOCK 0x08 /* Keep upper node locked on vput */
#define UN_CACHED 0x10 /* In union cache */
extern int union_allocvp __P((struct vnode **, struct mount *,
struct vnode *, struct vnode *,
struct componentname *, struct vnode *,
struct vnode *, int));
extern int union_copyfile __P((struct vnode *, struct vnode *,
struct ucred *, struct proc *));
extern int union_copyup __P((struct union_node *, int, struct ucred *,
struct proc *));
extern int union_dowhiteout __P((struct union_node *, struct ucred *,
struct proc *));
extern int union_mkshadow __P((struct union_mount *, struct vnode *,
struct componentname *, struct vnode **));
extern int union_mkwhiteout __P((struct union_mount *, struct vnode *,
struct componentname *, char *));
extern int union_vn_create __P((struct vnode **, struct union_node *,
struct proc *));
extern int union_cn_close __P((struct vnode *, int, struct ucred *,
struct proc *));
extern void union_removed_upper __P((struct union_node *un));
extern struct vnode *union_lowervp __P((struct vnode *));
extern void union_newlower __P((struct union_node *, struct vnode *));
extern void union_newupper __P((struct union_node *, struct vnode *));
extern void union_newsize __P((struct vnode *, off_t, off_t));
#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
#define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
#define UNIONTOV(un) ((un)->un_vnode)
#define LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
#define UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
#define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
extern int (**union_vnodeop_p)();
extern struct vfsops union_vfsops;
#endif /* KERNEL */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,495 @@
/*
* Copyright (c) 1994, 1995 The Regents of the University of California.
* Copyright (c) 1994, 1995 Jan-Simon Pendry.
* 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. 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.
*
* @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95
*/
/*
* Union Layer
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
#include <sys/queue.h>
#include <miscfs/union/union.h>
/*
* Mount union filesystem
*/
int
union_mount(mp, path, data, ndp, p)
struct mount *mp;
char *path;
caddr_t data;
struct nameidata *ndp;
struct proc *p;
{
int error = 0;
struct union_args args;
struct vnode *lowerrootvp = NULLVP;
struct vnode *upperrootvp = NULLVP;
struct union_mount *um = 0;
struct ucred *cred = 0;
struct ucred *scred;
struct vattr va;
char *cp;
int len;
u_int size;
#ifdef UNION_DIAGNOSTIC
printf("union_mount(mp = %x)\n", mp);
#endif
/*
* Update is a no-op
*/
if (mp->mnt_flag & MNT_UPDATE) {
/*
* Need to provide.
* 1. a way to convert between rdonly and rdwr mounts.
* 2. support for nfs exports.
*/
error = EOPNOTSUPP;
goto bad;
}
/*
* Get argument
*/
if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
goto bad;
lowerrootvp = mp->mnt_vnodecovered;
VREF(lowerrootvp);
/*
* Find upper node.
*/
NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
UIO_USERSPACE, args.target, p);
if (error = namei(ndp))
goto bad;
upperrootvp = ndp->ni_vp;
vrele(ndp->ni_dvp);
ndp->ni_dvp = NULL;
if (upperrootvp->v_type != VDIR) {
error = EINVAL;
goto bad;
}
um = (struct union_mount *) malloc(sizeof(struct union_mount),
M_UFSMNT, M_WAITOK); /* XXX */
/*
* Keep a held reference to the target vnodes.
* They are vrele'd in union_unmount.
*
* Depending on the _BELOW flag, the filesystems are
* viewed in a different order. In effect, this is the
* same as providing a mount under option to the mount syscall.
*/
um->um_op = args.mntflags & UNMNT_OPMASK;
switch (um->um_op) {
case UNMNT_ABOVE:
um->um_lowervp = lowerrootvp;
um->um_uppervp = upperrootvp;
break;
case UNMNT_BELOW:
um->um_lowervp = upperrootvp;
um->um_uppervp = lowerrootvp;
break;
case UNMNT_REPLACE:
vrele(lowerrootvp);
lowerrootvp = NULLVP;
um->um_uppervp = upperrootvp;
um->um_lowervp = lowerrootvp;
break;
default:
error = EINVAL;
goto bad;
}
/*
* Unless the mount is readonly, ensure that the top layer
* supports whiteout operations
*/
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
if (error)
goto bad;
}
um->um_cred = p->p_ucred;
crhold(um->um_cred);
um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
/*
* Depending on what you think the MNT_LOCAL flag might mean,
* you may want the && to be || on the conditional below.
* At the moment it has been defined that the filesystem is
* only local if it is all local, ie the MNT_LOCAL flag implies
* that the entire namespace is local. If you think the MNT_LOCAL
* flag implies that some of the files might be stored locally
* then you will want to change the conditional.
*/
if (um->um_op == UNMNT_ABOVE) {
if (((um->um_lowervp == NULLVP) ||
(um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
(um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
mp->mnt_flag |= MNT_LOCAL;
}
/*
* Copy in the upper layer's RDONLY flag. This is for the benefit
* of lookup() which explicitly checks the flag, rather than asking
* the filesystem for it's own opinion. This means, that an update
* mount of the underlying filesystem to go from rdonly to rdwr
* will leave the unioned view as read-only.
*/
mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
mp->mnt_data = (qaddr_t) um;
vfs_getnewfsid(mp);
(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
switch (um->um_op) {
case UNMNT_ABOVE:
cp = "<above>:";
break;
case UNMNT_BELOW:
cp = "<below>:";
break;
case UNMNT_REPLACE:
cp = "";
break;
}
len = strlen(cp);
bcopy(cp, mp->mnt_stat.f_mntfromname, len);
cp = mp->mnt_stat.f_mntfromname + len;
len = MNAMELEN - len;
(void) copyinstr(args.target, cp, len - 1, &size);
bzero(cp + size, len - size);
#ifdef UNION_DIAGNOSTIC
printf("union_mount: from %s, on %s\n",
mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
#endif
return (0);
bad:
if (um)
free(um, M_UFSMNT);
if (cred)
crfree(cred);
if (upperrootvp)
vrele(upperrootvp);
if (lowerrootvp)
vrele(lowerrootvp);
return (error);
}
/*
* VFS start. Nothing needed here - the start routine
* on the underlying filesystem(s) will have been called
* when that filesystem was mounted.
*/
int
union_start(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
return (0);
}
/*
* Free reference to union layer
*/
int
union_unmount(mp, mntflags, p)
struct mount *mp;
int mntflags;
struct proc *p;
{
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
struct vnode *um_rootvp;
int error;
int freeing;
int flags = 0;
#ifdef UNION_DIAGNOSTIC
printf("union_unmount(mp = %x)\n", mp);
#endif
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
if (error = union_root(mp, &um_rootvp))
return (error);
/*
* Keep flushing vnodes from the mount list.
* This is needed because of the un_pvp held
* reference to the parent vnode.
* If more vnodes have been freed on a given pass,
* the try again. The loop will iterate at most
* (d) times, where (d) is the maximum tree depth
* in the filesystem.
*/
for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
struct vnode *vp;
int n;
/* count #vnodes held on mount list */
for (n = 0, vp = mp->mnt_vnodelist.lh_first;
vp != NULLVP;
vp = vp->v_mntvnodes.le_next)
n++;
/* if this is unchanged then stop */
if (n == freeing)
break;
/* otherwise try once more time */
freeing = n;
}
/* At this point the root vnode should have a single reference */
if (um_rootvp->v_usecount > 1) {
vput(um_rootvp);
return (EBUSY);
}
#ifdef UNION_DIAGNOSTIC
vprint("union root", um_rootvp);
#endif
/*
* Discard references to upper and lower target vnodes.
*/
if (um->um_lowervp)
vrele(um->um_lowervp);
vrele(um->um_uppervp);
crfree(um->um_cred);
/*
* Release reference on underlying root vnode
*/
vput(um_rootvp);
/*
* And blow it away for future re-use
*/
vgone(um_rootvp);
/*
* Finally, throw away the union_mount structure
*/
free(mp->mnt_data, M_UFSMNT); /* XXX */
mp->mnt_data = 0;
return (0);
}
int
union_root(mp, vpp)
struct mount *mp;
struct vnode **vpp;
{
struct proc *p = curproc; /* XXX */
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
int error;
int loselock;
/*
* Return locked reference to root.
*/
VREF(um->um_uppervp);
if ((um->um_op == UNMNT_BELOW) &&
VOP_ISLOCKED(um->um_uppervp)) {
loselock = 1;
} else {
vn_lock(um->um_uppervp, LK_EXCLUSIVE | LK_RETRY, p);
loselock = 0;
}
if (um->um_lowervp)
VREF(um->um_lowervp);
error = union_allocvp(vpp, mp,
(struct vnode *) 0,
(struct vnode *) 0,
(struct componentname *) 0,
um->um_uppervp,
um->um_lowervp,
1);
if (error) {
if (loselock)
vrele(um->um_uppervp);
else
vput(um->um_uppervp);
if (um->um_lowervp)
vrele(um->um_lowervp);
} else {
if (loselock)
VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
}
return (error);
}
int
union_statfs(mp, sbp, p)
struct mount *mp;
struct statfs *sbp;
struct proc *p;
{
int error;
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
struct statfs mstat;
int lbsize;
#ifdef UNION_DIAGNOSTIC
printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
um->um_lowervp,
um->um_uppervp);
#endif
bzero(&mstat, sizeof(mstat));
if (um->um_lowervp) {
error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
if (error)
return (error);
}
/* now copy across the "interesting" information and fake the rest */
#if 0
sbp->f_type = mstat.f_type;
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
#endif
lbsize = mstat.f_bsize;
sbp->f_blocks = mstat.f_blocks;
sbp->f_bfree = mstat.f_bfree;
sbp->f_bavail = mstat.f_bavail;
sbp->f_files = mstat.f_files;
sbp->f_ffree = mstat.f_ffree;
error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
if (error)
return (error);
sbp->f_flags = mstat.f_flags;
sbp->f_bsize = mstat.f_bsize;
sbp->f_iosize = mstat.f_iosize;
/*
* if the lower and upper blocksizes differ, then frig the
* block counts so that the sizes reported by df make some
* kind of sense. none of this makes sense though.
*/
if (mstat.f_bsize != lbsize)
sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
/*
* The "total" fields count total resources in all layers,
* the "free" fields count only those resources which are
* free in the upper layer (since only the upper layer
* is writeable).
*/
sbp->f_blocks += mstat.f_blocks;
sbp->f_bfree = mstat.f_bfree;
sbp->f_bavail = mstat.f_bavail;
sbp->f_files += mstat.f_files;
sbp->f_ffree = mstat.f_ffree;
if (sbp != &mp->mnt_stat) {
sbp->f_type = mp->mnt_vfc->vfc_typenum;
bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
}
return (0);
}
/*
* XXX - Assumes no data cached at union layer.
*/
#define union_sync ((int (*) __P((struct mount *, int, struct ucred *, \
struct proc *)))nullop)
#define union_fhtovp ((int (*) __P((struct mount *, struct fid *, \
struct mbuf *, struct vnode **, int *, struct ucred **)))eopnotsupp)
int union_init __P((struct vfsconf *));
#define union_quotactl ((int (*) __P((struct mount *, int, uid_t, caddr_t, \
struct proc *)))eopnotsupp)
#define union_sysctl ((int (*) __P((int *, u_int, void *, size_t *, void *, \
size_t, struct proc *)))eopnotsupp)
#define union_vget ((int (*) __P((struct mount *, ino_t, struct vnode **))) \
eopnotsupp)
#define union_vptofh ((int (*) __P((struct vnode *, struct fid *)))eopnotsupp)
struct vfsops union_vfsops = {
union_mount,
union_start,
union_unmount,
union_root,
union_quotactl,
union_statfs,
union_sync,
union_vget,
union_fhtovp,
union_vptofh,
union_init,
union_sysctl,
};

File diff suppressed because it is too large Load diff